Swift UnwrapA blog about Swift, iOS, Xcode and overall architecture patterns. Unwrap your mind and learn new things!https://swiftunwrap.comenTue, 7 Apr 2026 12:42:05 +0000Tue, 7 Apr 2026 12:42:05 +0000250https://swiftunwrap.com/article/binding-side-effectThe unexpected @Binding side effectHow switching from a get-only Binding to a let variable might not be as innocent as you thinkhttps://swiftunwrap.com/article/binding-side-effectTue, 20 Jan 2026 00:00:00 +0000I recently stumbled upon a view defining a @Binding while never using the setter. Pretty much useless, don't you think?

Sure enough I just replaced it with a simple let. If we never use the setter, nothing could go wrong, right? Well of course... it did.

But why and how? Once stated it actually makes sense but this is something you probably didn't realise until now.

The scenario

Let's grab the culprit code: a simple view having a Boolean binding. When isAddingCard changes from true to false, the view gets animated.

struct CompetitionCardCollectionSlotView: View {
    let slot: PlayerCardCollectionSlot
    @Binding var isAddingCard: Bool

    @State var scaleEffect = CGFloat(1.0)
    @State var opacity = CGFloat(1.0)
    @State var brightness = CGFloat(0.0)

    var body: some View {
        cardView
            .onAppear(perform: {
                if isAddingCard {
                    scaleEffect = 1.3
                    opacity = 0
                    brightness = 0.7
                }
            })
            .onChange(of: isAddingCard) { oldValue, newValue in
                if oldValue, !newValue {
                    opacity = 1
                    withAnimation(.timingCurve(0.54, 0.05, 0.37, 0.98, duration: 0.3)) {
                        scaleEffect = 1
                        brightness = 0
                    }
                }
            }
        }
    }
}

#Preview {
    @Previewable @State var isAnimated = true

    VStack {
        CompetitionCardCollectionSlotView(slot: ..., isAddingCard: $isAnimated)
        Button(action: { isAnimated = false }) { Text("Start animation") }
    }
}

If we replace the binding with a simple let and try our preview... everything works 💪. So no issue for now.

What becomes interesting is where and how this view is used inside our codebase:

struct CompetitionCardCollection: View {
    let slots: [PlayerCardCollectionSlot]
    let addedCardSlots: [PlayerCardCollectionSlot]
    let startAnimation: Bool

    @State private slotsAnimated: [PlayerCardCollectionSlot.ID: Bool] = [:]

    var body: some View {
        ScrollView {
            VStack {
                ForEach(slots) { slot in
                    cardView(for: slot)
                }
            }
        }
        .onAppear {
            prepareAnimations()
        }
        .onChange(of: startAnimation) {
            if startAnimation {
                startAnimation()
            }
        }
    }

    @ViewBuilder
    func cardView(for slot: PlayerCardCollectionSlot) -> some View {
        let binding = Binding<Bool>(
            get: { slotsAnimated[slot.id, default: false] },
            set: { slotsAnimated[slot.id] = $0 }
        )
        CompetitionCardCollectionSlotView(slot: playerSlot, isAddingCard: binding)
    }

    func prepareAnimations() {
        for slot in addedCardSlot {
            slotsAnimated[slot.id] = true
        }
    }

    func startAnimation() {
        // we turn each slotsAnimated to false over time
        slotsAnimated[slot.id] = false
    }
}

This is an oversimplified version of the actual code but the global idea is:

  1. First, we render a list of slots. For each slot, we create a binding for isAddingCard using an attribute named slotsAnimated
  2. Inside onAppear, we prepare for the animation by filling slotsAnimated with the proper initial state
    • meaning some slots will change from false to true 3. When startAnimation turned true, we start the animation

Now if we replace isAddingCard: binding with just slotsAnimated[slot.id, default: false]... the animation does not work anymore 😦

We could argue about how CompetitionCardCollection animation is handled, whether we use the right default value and so on. But what strikes me here is: I changed a useless @Binding by a simple let... and it broke my code. WHY?

Time to investigate 🕵🏻‍♂️

Debugging

As any good developer, best approach is by using my good old friend print 😜. Let's first see what's happening inside the view rendering cycle methods and look if we see any difference between the two implementations:

cardView
    .onAppear {
        print("[onAppear]")
        print("[onAppear] isAddingCard = \(isAddingCard)")
    }
    .onChange(of: isAddingCard) { oldValue, newValue in
        print("[onChange] \(oldValue) -> \(newValue)")
    }
// With @Binding
[onAppear]
[onAppear] isAddingCard = true
[onChange] false -> true

// With let
[onAppear]
[onAppear] isAddingCard = false
[onChange] false -> true

Notice how we don't get the same value inside onAppear. There's definitely something going on. Did you also notice that, when using Binding, isAddingCard is already true inside onAppear and yet onChange is called. How is it possible?

Well there's one missing log we could add and would unravel the whole mystery: inside the binding.

// with binding
let binding = Binding<Bool>(
    get: {
        print("[getter] \(slotsAnimated[slot.id, default: false])")
        return slotsAnimated[slot.id, default: false]
    },
    set: { slotsAnimated[slot.id] = $0 }
)
CompetitionCardCollectionSlotView(slot: playerSlot, isAddingCard: binding)

// with no binding
// using a closure so we can use print inside a view
let isAddingCard = {
    print("[getter] \(slotsAnimated[slot.id, default: false])")
    return slotsAnimated[slot.id, default: false]
}

CompetitionCardCollectionSlotView(slot: playerSlot, isAddingCard: isAddingCard())
// With binding
[getter] false
[onAppear]
[getter] true
[onAppear] isAddingCard = true
[getter] true
[onChange] false -> true

// With no binding
[getter] false
[onAppear]
[onAppear] isAddingCard = false
[getter] true
[onChange] false -> true

Now everything becomes crystal-clear 🔮:

  • inside onAppear using Binding, requesting isAddingCard we're effectively calling a closure which returns the current value. Hence we're getting true because the value had changed since the view rendering was planned.
  • Using let, we get a snapshot so the value inside onAppear is the one the view was created with, which is the value we'd "expect".

So Binding has a (subtle) side effect I didn't realize until now which might lead to get a value sooner (inside the rendering cycle) than expected!

What is AI saying about it?

It's AI era (it seems?) and so you may be thinking "couldn't we have solved it using AI?" 🤖. I believe it's interesting to first look by yourself then asking AI if you failed to find the reason or just want a counter argument.

And so was AI able to understand the issue? Here was my prompt:

I found a strange behaviour inside CompetitionCardCollectionSlotView:

- if I replace isAddingCard binding by a simple bool value, then the animation inside CompetitionCardCollectionView doesn't work anymore.

Can you understand and explain the reason why the logs are different? What's the reason?

I tested twice: only the prompt, and the prompt + logs. In first case the answer seemed a little bit clunky while for the later AI provided a far more decent response:

### With Binding (Working Animation)

When you use a `Binding<Bool>` with a custom `get` closure:

1. SwiftUI calls `get` during view evaluation to establish dependencies.
2. The multiple `get` calls you see are SwiftUI building its dependency graph.
3. When `slotsAnimated[slot.id]` changes, SwiftUI detects it and re-evaluates the view.
4. `onAppear` sees `true` because the binding reads the current value at that moment.
5. `onChange` fires because the binding’s value changes from `true` to `false`.

### With Simple Bool (Broken Animation)

When you use a simple bool value (line 243: `let isAddingCard = slotsAnimated[slot.id]`):

1. The value is captured once when the view is created.
2. SwiftUI doesn’t track changes to `slotsAnimated[slot.id]` because it’s not a tracked dependency.
3. `onAppear` sees `false` because it reads the initial captured value.

Which is what we also concluded from our test. HOWEVER it came with the worst advice possible: "Keep using the binding pattern" 🔫.

Conclusion

This was super interesting to debug because it made me realize one thing which was implicit until now: when using a Binding, the value you get may vary during the rendering cycle. Which I think might lead to some subtle bugs from time to time?

I guess that would be a good reason to prefer having two variables over a Binding: a plain let attribute along a setter closure.

]]>
https://swiftunwrap.com/article/running-package-from-xcode-build-phaseRunning a package from Xcode Build phaseYou’ve been using a SPM package in Xcode and desperately wonder how to do swift run package with it? Then here’s the answer!https://swiftunwrap.com/article/running-package-from-xcode-build-phaseTue, 25 Jun 2024 00:00:00 +0000I’ve been recently struggling with Xcode while I was facing (twice) the need of running a SPM package from it. First with Apollo after migration to 1.x and which does no more support generating models from Build phase. Then with https://github.com/pjechris/AnnotationInject (my homemade dependency injection library) where someone also asked how to integrate it with Xcode.

After many searches and failures here is the final solution I came up with!

The problem

But first let me explain you why I needed to do this. Thos libraries don’t have SPM Build plugin support (which is not a great tool, sorry!) and so I needed to do swift package run in order to use them.

But anyone who ever tried to run swift package run <my_package> from Build Phase knows that… it just plainly fails 😬

Why is it so? I suspect Apple felt it would be fun for developers to have different behaviours when including packages either using Package.swift or Xcode 🤭: indeed using Package.swift you get your dependencies downloaded into the root tree while with Xcode… it’s just somewhere. And finding where this somewhere is is most of the job!

Où k’il est ?

Where is Xcode hiding storing those packages? It wasn’t easy to figure out because it’s not documented and no environment variable ever makes reference to it 😤.

After searching for a while it actually seems those are stored in <derived_data>/<project>/SourcePackages.

Luckily for us… no variable ever refer to derived_data ever 😄. I’m starting to think Apple loves developers 🥹❤️.

We’ll have to rely on some other environment variable, BUILD_DIR, and tweak it a little bit. I ended up with this script (for AnnotationInject), hoping it would work 🤞:

SPM_CHECKOUT_DIR=${BUILD_DIR}/../../../Build/SourcePackages/checkouts/AnnotationInject
cd $SPM_CHECKOUT_DIR
/usr/bin/xcrun swift run annotationinject-cli ...

But… it didn’t 😅. Xcode just throws an error: « using sysroot for 'iPhoneSimulator' but targeting 'MacOSX' ». Some google search later, setting the SDK to macOS fixes the issue:

SPM_CHECKOUT_DIR=${BUILD_DIR}/../../../Build/SourcePackages/checkouts/AnnotationInject
cd $SPM_CHECKOUT_DIR
/usr/bin/xcrun --sdk macosx swift run annotationinject-cli ...

And it’s working… for now! You thought it was ever? It’s not 😄.

What about production?

So everything is working… until you decide to release your app. And then something terrible happens: the script doesn’t work 😱.

The reason is pretty « simple » : BUILD_DIR doesn’t match the same patch when compiling in RELEASE mode so our little trick don’t work anymore 😈.

Thankfully the solution is, again, pretty simple:

SPM_CHECKOUT_DIR=${BUILD_DIR%Build/*}SourcePackages/checkouts/AnnotationInject
cd $SPM_CHECKOUT_DIR
/usr/bin/xcrun --sdk macosx swift run annotationinject-cli ...

Using this syntax trims everything after Build inside the variable, which allows to adapt seamlessly for both DEBUG and RELEASE 🎉.

Bonus : Apollo

If you ever need, here is the script I wrote for downloading apollo cli and generating files:

SPM_CHECKOUT_DIR=${BUILD_DIR%Build/*}SourcePackages/checkouts/apollo-ios

if [ ! -f ${SRCROOT}/apollo-ios-cli ]; then
    echo "Downloading missing apollo cli"
    sh ${SPM_CHECKOUT_DIR}/scripts/download-cli.sh ${SRCROOT}
fi

cd ${SRCROOT}

./apollo-ios-cli generate

find Generated/Apollo -type f > .apollo_generated.xcfilelist

Note the last line. By generating a xcfilelist containing all generated filenames we can add them to Xcode with no versioning while also supporting fresh builds (when files were not generated yet).

Conclusion

Now you know it’s possible to use swift run from Build Phase and how to do so. It can be pretty useful when having to run a package not supporting SPM Command plugin.

It’s just a shame we have nothing more «straightforward». Hopefully Xcode integration will get better one day like… allowing to use our own Package.swift? 🤷‍♂️

]]>
https://swiftunwrap.com/article/dangerswiftDangerSwiftA very brief introduction to DangerSwift and how to use it to automate repetitive tasks for your merge requests.https://swiftunwrap.com/article/dangerswiftTue, 16 Apr 2024 00:00:00 +0000Have you ever heard about Danger? It’s a tool created by Orta allowing to use your merge request metadata to automate reviewing such as linting, naming convention, …

Today I’ll show you its Swift counterpart: DangerSwift. See this article as a cheat sheet… for myself 🤣: while the tool is not complex we/I tend to always forget how to use it.

Here are the steps we’ll see:

  1. Installing the tool
  2. Making our own Dangerfile.swift
  3. Testing locally
  4. Connecting the tool to our CI

Installation

You have plenty of possibilities to install the tool:

  • 📦 SPM… but according to the doc you’ll have to install DangerJS first 🤔
  • 🏃‍♂️ Marathon… which has been deprecated
  • 🍺 Homebrew

So my advice is simple: go with homebrew.

Dangerfile

Dangerfile.swift contains all your validation rules. Avoid creating it by hand and rather prefer using danger-swift edit command. This will launch Xcode with your configuration file.

Even better, you’ll get autocompletion! In my opinion this is super useful and the biggest advantage of DangerSwift over Danger Ruby or Danger JS.

There is no rule on how to make your danger file. But here how I do it:

  1. Create a function per rule.
  2. Call each rule at the beginning of the file.

Here’s a small snippet:

let danger = Danger()

// 2. Call each rule here
checkMRTitleConvention(danger)
checkPatchIsOnReleaseBranch(danger)
checkViewModelsAreAnnotatedMainActor(danger)

/// 1. One rule == One function

/// Checks the merge request title follow our convention
/// i.e, feat|tech|fix(<title>): [IOS-XX] <description>
func checkMRTitleConvention(_ danger: DangerDSL) {
  let title = Regex {
    Optionally { "Draft: " } // if MR is marked as draft on Gitlab
    ChoiceOf {
      "feat"
      "tech"
      "fix"
    }
    "("
    OneOrMore(.word)
    ")"
    ": [IOS-"
    OneOrMore(.digit)
    "] "
    OneOrMore(.word)
  }

  guard danger.gitLab.mergeRequest.title.wholeMatch(of: title) != nil else {
    fail("Title is not respecting convention: feat|tech|fix(<title>): [IOS-XX] <description>")
    return
  }
}

Test

Having validation rules is one thing, but having validation rules working is far better!

To test your code you test locally and check the result: danger-swift local -b master -d Dangerfile.swift. Unfortunately this command crashes as soon as start using GitLab/Github DSL (so pretty quickly).

Only solution is to test on an existing merge request using danger-swift pr <url> . However this command will require having an Access Token, just like on CI.

Continuous integration

Whether being for local test on merge requests or from CI, you’ll need an Access Token.

Documentation is pretty clear on how to generate a token, so just follow it. You then just have to run danger-swift ci .

Command have multiple options which can be useful:

  • --removePreviousComments removes comments made by previous build allowing to have at most one report at a time.
  • --danger_id=xx gives a unique ID to your comment. This is useful if you decide to run Danger at multiple steps, like at the beginning of the build for validating and at the end for generating test report.
  • -d path to your Dangerfile. Very useful when using DangerSwift from fastlane 😉

CheatSheet

Let’s summarize all this as a cheatsheet:

  1. 🍺 Setup using homebrew
  2. ✏️ Use danger-swift edit to edit your Dangerfile
  3. 🔨 Generate an https://danger.systems/js/guides/getting_started.html DANGER_XX_API_TOKEN
  4. ✅ Test locally
    1. danger-swift local -b <branch>
    2. danger-swift pr <url>
  1. 🤖 Run on CI
    • danger-swift ci -f --removePreviousComments --danger_id=<unique_name> -d Dangerfile.swift
]]>
https://swiftunwrap.com/article/comparing-equatable-using-opened-existentialsComparing Equatable using Opened ExistentialsHow can we compare two objects with unknown types? By leveraging latest Swift features!https://swiftunwrap.com/article/comparing-equatable-using-opened-existentialsTue, 20 Feb 2024 00:00:00 +0000If you’ve been in Swift ecosystem for many years then you at least encountered this error once: “Protocol ‘XX’ can only be used as a generic constraint because it has Self or associated type requirements”. Maybe you even had nightmares about it 👻!

It’s indeed one of the most common issue developers face while learning the language. And until not so long ago, it was impossible to “fix”: you had to rethink your code by avoiding casting an object to an existential.

Thankfully this time is now over! Let’s acclaim our saviour: any. We’ll dive into a real usecase (comparing two Equatable objects) to understand how it can be used to solve our issues.

The Swift Programming Guide defines an existential as such: “A boxed protocol type is also sometimes called an existential type, which comes from the phrase ‘there exists a type T such that T conforms to the protocol’”. In other words: we talk about existential when trying to use a protocol as a concrete type (function signature, casting, …).

Equatable

Let’s create a enum with associated value that we’d like to conform to Equatable.

enum ServiceState: Equatable {
  case failed(Error? = nil)
}

While in most cases Swift would be able to automatically synthesize the requirement, Error not being Equatable prevents default implementation from being generated 🥲. So we’ll have to do it by hand:

enum ServiceState: Equatable {
  case failed(Error? = nil)

  static func == (lhs: ServiceState, rhs: ServiceState) -> Bool {
    switch(lhs, rhs) {
    case (.failed(let lhsError), .failed(let rhsError)):
      if let lhsError = lhsError as? Equatable, let rhsError = rhsError as? Equatable {
        return lhsError == rhsError
      }
      return false
    }
  }
}

This code won’t be compile though. On old Swift version you’ll get the infamousProtocol ‘Equatable’ can only be used as a generic constraint” message. But starting with Swift 5.6 you’ll get another one: “Use of protocol 'Equatable' as a type must be written 'any Equatable’”.

That’s interesting! Let’s just follow Swift recommendation:

if let lhsError = lhsError as? any Equatable, let rhsError = rhsError as? any Equatable {
	return lhsError == rhsError
}

But that doesn’t compile either. Now you’ll have “Binary operator '==' cannot be applied to two 'any Equatable' operands” error 😖.

But that make sense. Equatable is expecting to receive two objects whose types are identical. Here we have two Equatable objects… but they might still be of different types! How to solve this 🧐? You got it: but using opened existentials!

Open Sesame!

The idea of opened existential is to… open an existential 🤷‍♂️. In other words: gaining access to its internal type (Self). And what better candidate to access Self than… the type itself? Sounds confusing but it's actually pretty straightfoward:

  1. You add a function to your type (protocol)
  2. Inside this function you use Self

Let’s apply this to our example:

  1. We define a function Equatable.isEqual
  2. We use it to access Self which is the object concrete type
  3. We compare it to our parameter (rhs)
  4. We use the new function instead of ==
extension Equatable {
  fileprivate func isEqual(_ rhs: any Equatable) -> Bool {
	// A. here we can access Self and check if rhs has the same type than ours
    if let rhs = rhs as? Self, rhs == self {
      return true
    }
    return false
  }
}

enum ServiceState: Equatable {
  case failed(Error? = nil)

  static func == (lhs: ServiceState, rhs: ServiceState) -> Bool {
    switch(lhs, rhs) {
    case (.failed(let lhsError), .failed(let rhsError)):
      if let lhsError = lhsError as? any Equatable, let rhsError = rhsError as? any Equatable {
		// B. now we use our `isEqual` function instead of `==`
        return lhsError.isEqual(rhsError)
      }
      return false
    }
  }
}

And now this compiles 🎉!

But this code may look a little bit weird to you as we moved the logic inside the protocol. One other option is to use Implicitly Opened Existentials.

Implicitly Opened Existentials

We went from existentials > opened existentials > implicitly opened existentials! I know what you’re thinking: it starts to look like Dragon Ball 🐉 transformations! But I promise: it stops there 😄

Swift 5.7 introduced generic support with existentials: now you can pass an existential as a function generic parameter.

Let’s take back our example and this time instead of extending our protocol we’ll just create a generic function checkIsEqual:

func checkIsEqual<A: Equatable, B: Equatable>(_ a: A, _ b: B) -> Bool {
  if let b = b as? A, b == a {
    return true
  }

  return false
}

enum ServiceState: Equatable {
  case failed(Error? = nil)

  static func == (lhs: ServiceState, rhs: ServiceState) -> Bool {
    switch(lhs, rhs) {
    case (.failed(let lhsError), .failed(let rhsError)):
      if let lhsError = lhsError as? Equatable, let rhsError = rhsError as? Equatable {
        return checkIsEqual(lhsError, rhsError)
      }
      return false
    }
  }
}

Here you see that we got implicit access to our two existential types throught A and B. Hence the name of this functionality.

Conclusion

You now know how to compare two equatable objects in Swift and better understand how to leverage any in your codebase and how helpful it can be. Whether you prefer using solution 1 (extending protocol) or solution 2 (implicitly opened existential) will mostly be a matter of taste.

]]>
https://swiftunwrap.com/article/unsafe-memory-mutationUnsafe memory mutationSometimes your memory can play nasty tricks on you… Let’s have fun with it by having a look on how we can mutate… immutable properties!https://swiftunwrap.com/article/unsafe-memory-mutationTue, 24 Oct 2023 00:00:00 +0000Sometimes your memory can play nasty tricks on you…

Today let’s have fun with memory management by having a look on how we can mutate… immutable properties!

How is it possible?

Swift is a safe language. One such example is when declaring immutable properties: when doing so Swift compiler then provides restrictions/verifications to make sure those data stay indeed immutable.

However those restrictions are true only while compiling. During runtime you are “free” of playing with memory however you want! Using tools you can read/write it and of course… making your app crash 😉💥

Swift provides such tools. Compared to some other languages they are very interesting because they provide two abstraction level:

  1. First level preserves memory typing while manipulating memory, still providing a very safe API
  2. Second one provides raw pointers

So we could say that in addition of classic/high level API Swift also provides some mid and low level functions.

What’s the purpose?

Let’s be honest… not much 😂. At least there’s very few chance you ever need to use such an API one day (if you stick to building high level apps).

However not only can it be fun to study it might come handy in some libraries. For example we may need at some point to modify… an immutable property 🤪

struct MyStruct {
  let isImmutable = true
}

var value = MyStruct(isImmutable: true) // let's try mutate... isImmutable 🤪

Let there be carnage var

Let’s first get a UnsafeMutablePointer<MyStruct> from our objects using withUnsafeMutablePointer:

withUnsafeMutablePointer(to: &value) { }

This pointer indicates where our object is located in memory. Now we need to locate isImmutable property itself… and mutate it.

We could do some hazardous maths 🧮 to get memory offset but Swift does already provide everything we need with MemoryLayout: given a KeyPath it gives us its corresponding offset 💪.

We can then combine it with UnsafeMutableRawPointer. As its name implies we lose typing but we gain access to advanced(by:) allowing us to move to a memory specific position. Once there we just have to do the mutation 😎

withUnsafeMutablePointer(to: &value) {
	let pointer = UnsafeMutableRawPointer($0)

	// get isImmutable offset in memory
	guard let offset = MemoryLayout<MyStruct>.offset(of: \.isImmutable) else {
		fatalError("Cannot use KeyPath<\(MyStruct.self), \(Value.self)> which is a computed KeyPath")
	}

	advanced(by: offset) // move to isImmutable in memory
		.assumingMemoryBound(to: Bool.self) // cast memory to Bool
		.pointee = false // change the memory!
}

print(value.isImmutable) // prints "false"

Et voila! Our property now equals false even though it was initially declared as let and supposed to be immutable! 🧙‍♂️

While the code is not very complex we can actually do even simpler. Swift already provides a method to point directly to a KeyPath position. Best thing: it’s available on UnsafeMutablePointer, so no typing loss!

withUnsafeMutablePointer(to: &value) {
	/// get memory pointer related to our keypath
	guard let keyPathPointer = $0.pointer(to: \.isImmutable) else {
		fatalError("Cannot use KeyPath<\(MyStruct.self), \(Value.self)> which is a computed KeyPath")
	}

	/// get a mutable pointer from our (non mutable) pointer
	let pointer = UnsafeMutablePointer(mutating: keyPathPointer)

	/// mutate memory
	pointer.pointee = false
}

Thanks to pointer(to:) we got a (non mutable) pointer to our property. We then changed it to a mutable one to mutate the memory.

Pretty simple? But believe me before getting to this… I spent countless hours reading the documentation 📚

Array modification

While modifying a property is easy, modifying an array is one magnitude more complex.

If we apply same code on Wrapper it will just crash:

struct Wrapper {
  let array = ["a", "b"]
}

withUnsafeMutablePointer(to: &value) {
  UnsafeMutablePointer(mutating: $0.pointer(to: \.array[0])!) // crash

  pointer.pointee = "c"
}

In order to mutate Wrapper.array we first we need to get array property and then change data in index 0. Don’t expect help on UnsafeMutable*Pointer. You’ll need to use an obscure method from MutableCollection: withContiguousMutableStorageIfAvailable !

withUnsafeMutablePointer(to: &value) {
	let pointer = UnsafeMutablePointer(mutating: $0.pointer(to: \.array)!)

	pointer.pointee.withContiguousMutableStorageIfAvailable {
	  $0[0] = "c"
  	}
}

print(value) // prints ["c", "b"]

And now we can mutate our marvelous array!

Generic

We can make those two samples more generic. First for a non collection value:

extension UnsafeMutablePointer {
  func assign<Value>(to keyPath: KeyPath<Pointee, Value>, value: Value) {
    guard let keyPathPointer = pointer(to: keyPath) else {
      fatalError("Cannot use KeyPath<\(Pointee.self), \(Value.self)> which is a computed KeyPath")
    }

    let pointer = UnsafeMutablePointer<Value>(mutating: keyPathPointer)

    pointer.pointee = value
  }
}

var value = MyStruct()

withUnsafeMutablePointer(&value) {
	$0.assign(to: \.isImmutable, value: false)
}

For collection we need to be careful to constrain to MutableCollection! Indeed withContiguousMutableStorageIfAvailable also exists on Collection but does… nothing. Yes I got tricked by this one 😬

extension UnsafeMutablePointer {
	func assign<C: MutableCollection>(to keyPath: KeyPath<Pointee, C>, index: C.Index, value: C.Element) where C.Index == Int {
    guard let keyPathPointer = pointer(to: keyPath) else {
      fatalError("Cannot use KeyPath<\(Pointee.self), \(C.self)> which is a computed KeyPath")
    }

    let pointer = UnsafeMutablePointer<C>(mutating: keyPathPointer)

    pointer.pointee.withContiguousMutableStorageIfAvailable {
      $0[index] = value
    }
  }
}

var value = Wrapper()

withUnsafeMutablePointer(&value) {
	$0.assign(to: \.array, index: 0, value: "c")
}

Conclusion

This is probably the most enlightening yet non useful article you’ll ever read 😄.

Still now you know it’s perfectly possible to alter memory in Swift including non mutable properties 🙂

]]>
https://swiftunwrap.com/article/fastlane-app-signingManaging app signing in fastlaneApp signing is probably the biggest nightmare of iOS developers. Time to face your fear and master it!https://swiftunwrap.com/article/fastlane-app-signingTue, 5 Sep 2023 00:00:00 +0000If you read Advanced fastlane you now masterize configuring the tool with ease. Congratulations! However one question might still rise: how to sign your damned beautiful application with no effort?

Let’s see some of the possibilities offered by Fastlane and iOS ecosystem. Disclaimer: code signing is hell! 👿

Signing a single app

Let’s start “simple”, shall we? We’ll try to sign a single application, meaning no embedded extensions inside it (such as Widgets). In this configuration there are mainly two teams: those using fastlane match, and those using Cert and Sigh.

match

With fastlane match you can get your certificates and provisioning profiles in one command: match(). Once every thing is downloaded you can build your app.

You can also enforce the provisioning profile to sign with, making sure it uses the one you just downloaded. There are 3 ways of doing it:

  1. 🛠️ By setting it in xcodeproj. Definitely not a good way
  2. 🗃️ Using a xcconfig
  3. 🛣️ Directly into your lane

For now we’ll go with option 3:

lane :build do
	require_envs(:MATCH_TYPE, :APP_IDENTIFIER)

	match()

	xcargs = {
      	:CODE_SIGN_STYLE => "Manual", #disable automatic signing
      	:PROVISIONING_PROFILE_SPECIFIER => lane_context[:MATCH_PROVISIONING_PROFILE_MAPPING][ENV['APP_IDENTIFIER']]
  	}

	gym(xcargs: xcargs)	 # xcargs allows us to give additional options to xcodebuild
end

Instead of xcargs you can use actions like update_code_signing_settings. I prefer xcargs because it doesn’t mutate my xcodeproj. However it also implies you did not change it manually either so that command line settings can take effect 😉

Many people like to use fastlane match because your certificates and provisioning profile can be accessed through teammates by getting stored (usually on a Git repository). While it’s useful for the private key it’s pretty much useless for provisioning profiles: they are already available on iOS dev portal.

One disadvantage about match is that you can’t renew anything automatically: neither certificate nor provisioning profiles (except in some rare cases). This could be OK if you did not have to first nuke the files you versioned on your Git repository in order to upload the new ones (that you generated manually). All in all it adds lots of additional steps for nothing.

Another big issue I faced with match: you don't own the cycle. Everything is done some way very specific and pushed directly on remote repository. If for any reason you have restricted permissions it can quickly become nightmare.

Cert + Sigh

Another solution is by using Cert and Sigh:

  • Cert is responsible for downloading certificate related to your app
  • While Sigh will download the provisioning profile
lane :build do
	cert()
	sigh()

	xcargs = {
      	:CODE_SIGN_STYLE => "Manual", #disable automatic signing
      	:PROVISIONING_PROFILE_SPECIFIER => lane_context[:SIGH_NAME]
  	}

	gym(xcargs: xcargs)
end

Cert and Sigh might not be as trendy as match but they have the advantage of downloading everything from iOS dev portal: you therefore only have one source of truth. You can even regenerate them on fly if needed. It’s definitely handy for provisioning profiles (but I would discourage doing it for certificates).

Their only downside is that you have to manage private certificate key yourself. Which is something I consider as non troublesome when you use a tool like Bitrise.

Signing an app with extensions

Up until now this was the “easy” case. But as soon as you introduce app Extensions things become tougher.

Time to get dirty!

Let’s say we have an app “com.foo.bar” with an extension “com.foo.bar.widgetExtension”. If we try the code from previous section we’ll get an error: Provisioning profile "com.foo.bar XX" has app ID "com.foo.bar", which does not match the bundle ID "com.foo.bar.widgetExtension"

Xcode is actually… trying to use our app provisioning profile to sign our extension 🤯. We indeed did not download our extension provisioning profile. To fix this let’s first try a naive approach:

lane :build do
	cert()
	sigh()
	sigh(app_identifier: "com.foo.bar.widgetExtension") # we download our extension profile

	xcargs = {
      	:CODE_SIGN_STYLE => "Manual", #disable automatic signing
      	:PROVISIONING_PROFILE_SPECIFIER => lane_context[:SIGH_NAME]
  	}

	gym(xcargs: xcargs)
end

Still not compiling! Now we get… Provisioning profile "com.foo.bar.widgetExtension YY" has app ID "com.foo.bar.widgetExtension", which does not match the bundle ID "com.foo.bar". What? That’s the exact opposite of previous error!

It actually makes sense. When using lane_context[:SIGH_NAME] we’re referring to the last downloaded profile… which is the one for our extension!

With match the error would be the same as the first one as we explicitly use our app identifier.

Let’s try something else: let’s not enforce provisioning profile and use automatic signing:

lane :build do
	cert()
	sigh()
	sigh(app_identifier: "com.foo.bar.widgetExtension") # we download our extension profile

	xcargs = {
      	:CODE_SIGN_STYLE => "Automatic"
  	}

	gym(xcargs: xcargs)
end

And this time… 🥁… it works! 🎉 So while enforcing profile for a single target is OK, it can become troublesome when having multiple ones.

However we now have no way of making sure the app was signed with the just recently downloaded provisioning profile. We can workaround this by adding a verification step at the very end:

lane :build do
	require_envs(:APP_IDENTIFIER)

	cert()
	sigh()

	app_provisioning_uuid = ENV['SIGH_UUID']

	sigh(app_identifier: "com.foo.bar.widgetExtension") # we download our extension profile

	xcargs = {
      	:CODE_SIGN_STYLE => "Automatic"
  	}

	gym(xcargs: xcargs)
	verify_build(bundle_identifier: ENV['APP_IDENTIFIER'], provisioning_uuid: app_provisioning_uuid)
end

Let Xcode do the heavy job

Some time ago Xcode introduced a new option: allowProvisioningUpdates. This option is supposed to let Xcode download (or generate) certificates and provisioning profiles. Using it we can get rid of cert, sigh… or match!

lane :build do
	require_envs(:APP_IDENTIFIER)

	xcargs = "-allowProvisioningUpdates"
  	xcargs += " -authenticationKeyID #{ENV['APP_STORE_CONNECT_API_KEY_KEY_ID']}"
  	xcargs += " -authenticationKeyIssuerID #{ENV['APP_STORE_CONNECT_API_KEY_ISSUER_ID']}"
  	xcargs += " -authenticationKeyPath '#{ENV['APP_STORE_CONNECT_API_KEY_KEY_FILEPATH']}'"
	xcargs += " CODE_SIGN_STYLE=Automatic"

	gym(xcargs: xcargs)
end

Note the usage of authentication*** parameters: this is needed to give Xcode an API token in order for allowProvisioningUpdates to work.

Also note that authenticationKeyPath expects an absolute path so you might want to check it:

key_file_path = Pathname.new(ENV['APP_STORE_CONNECT_API_KEY_KEY_FILEPATH'])

UI.user_error "APP_STORE_CONNECT_API_KEY_KEY_FILEPATH must be absolute" if key_file_path.relative?

xcargs += " -authenticationKeyPath #{key_file_path}"

If you run it then you should see… your app built and archived! 🎉

Getting “There are no accounts registered with Xcode” on CI? You probably wrote -authenticationKeyID=XX instead of -authenticationKeyID XX

Automatic or Manual?

We saw signing using both automatic and manual.

Automatic has the big advantage of requiring less configuration. But you obviously have less control about which provisioning profile will be used by Xcode.

Manual is the opposite: you have to clearly declare which provisioning profile to use. However it can be hard to configure when having multiple targets. In this scenario you won’t be able to rely much on fastlane and will have no choice but to configure provisioning profiles per target inside Xcode, using Build Settings or xcconfig files:

# TARGET A.xcconfig
PROVISIONING_PROFILE_SPECIFIER = myProfile

# TARGET B.xcconfig
PROVISIONING_PROFILE_SPECIFIER = anotherProfile

Code signing is still something hard to get correctly especially when having extensions. But things got easier with tools like match or Cert + Sigh. Or even with Xcode, now handling signing for you right from your command line! 👏

In any case best thing is to configure signing as less as possible and let those tools handle all the rest.

]]>
https://swiftunwrap.com/article/data-vs-domainData and Domain models: Learn to use themModeling is not an easy task, especially if you don't know there are many kind of models that exist! Let's explore them and how to use them.https://swiftunwrap.com/article/data-vs-domainTue, 27 Jun 2023 00:00:00 +0000With SwiftData coming out many people will throw themselves into making a “model”.

But did you ever think what is a model? Or what it its role or objective?

Like when talking about AI there are many kinds of models! Ok maybe not that many… today I'll talk the two I know of and use: Data (DTO) and Domain.

Never heard about them? Then there’re a high chance you did a hybrid one by mixing the two concepts 👽. Let see what makes them so different and how/when you should use them.

Data: all you data are belong to us

Let’s start with the most usual one: Data model aslo known as DTO (Data Transfer Object). It represents where data are coming from (or going to): HTTP, database, filesystem… All those technologies stores data. So whenever you use them to read/write data you effectively use a DTO.

Here a some of them:

  • 🌐 HTTP requests
  • 🗄️ CoreData (or now SwiftData)
  • 🗃️ Realm
  • 🔑 Keychain
  • 👤 UserDefaults

The last two one might be more surprising to some of you. Indeed you store and read data from keychain and UserDefaults. So you do use (or should use) Data models when doing so.

/// an HTTP response model
struct GetUserDTO: Decodable {
	let userId: Int
	let firstname: String
	let lastname: String
  let companyId: Int
  let companyName: String
  let street: String
	let zipcode: String
  let country: String
  let preferredLanguage: String
  let darkMode: Bool
}

/// user OAuth credentials DTO stored into keychain
struct OAuthKeychainDTO: Codable {
	let token: String
	let refresh: String
	let expireAt: Date
}

One important thing to note about DTO: it heavily depends on your tech architecture 🦕. Any change to your tech stack has an impact on them.

For instance the fact you decide to use a database implies you might no be able to use some Swift primitives (like enum) or that you’ll have to do relationship tables hence a another Data model and so on.

Another example is about changing your stack: migrating from CoreData to Realm, from REST to GraphQL… all those changes impact your tech architecture therefore your Data models.

Where to use Data models?

Because of such dependency you should not use DTO into your business layers (views, view models, etc…). It might sounds obvious to some of you but I recall that apple shows examples and provides helpers to easily use CoreData/SwiftData objects from… SwiftUI views! It’s probably a easy and great to learn Swift and SwiftUI and to build prototypes or small apps but definitely not a good approach for long term apps 😉

As such DTO should only their own dedicated stack which I call Data source. In other words: no one but your CoreDataSource should use a CoreData model. One good way to « enforce » this rule is by building each DataSource as a package 📦 and keep DTO internal.

You might want to put a tool like Danger to force these rules. Otherwise one developer will surely put a DTO as public one day or another.

But if DTO are internals how do you send back “data” to upper layers? Domain model to the rescue!

Domain model

Domain is not DTO

Domain models are an abstraction on top of DTO. Be careful though: it’s not because you « abstracted » your DTO that you effectively built a Domain model! I see you SwiftData!

A Domain depicts data meaning: what should be its role and usage in your application. For instance: what does it mean to be a User? What does it mean to have Settings? When can I change them?

Compared to DTO Domain models have no dependencies (apart from core library like Foundation). And because they are built on top of nothing they are tech agnostic.

In other words: changing your tech implementation must have zero impact on it. So if you use SwiftData… you’re actually still building Data models. And that’s OK but don’t confuse it with a Domain model.

Domain is simple

Many things can change from your DTO to your Domain model implementation:

  1. 📛 Naming. In your Domain you’re allowed to « fix » naming (for example wrong spelling from the backend)
  2. 🧲 Composition/aggregation. You can split your objects into multiple smaller Domain objects. Doing so can greatly improve your code maintenance and avoid nesting relationships.
  3. 🦍 Strongly type attributes. Have a finite list of value? Use a enum. A date? Use Date, etc… Strongly typing reduce the numbers of invalid case you could have in your app making it less buggy.
  4. ❓Avoid optional properties when not needed. Associated values can be handy

What is a good Domain?

Making good Domain object is not that easy. Based on the rules edicted before here is some examples of good and abad modelling:

/// BAD
/// It's just a 1-1 with DTO with all attributes
struct User {
	let id: Int
	let firstname: String
	let lastname: String
  let companyId: Int
  let companyName: String
  let street: String
	let zipcode: String
  let country: String
  let preferredLanguage: String
  let darkMode: Bool
}

/// BAD
/// - attributes are nested
/// - Non useful parameters NOT DEFINING the user identity (useDarkMode, address)
/// - Some parameters can be unclear purposes (useDarkMode? What's this?)
struct User: Identifiable {
	let id: UUID
	let firstname: String
	let lastname: String
	let email: String
	let company: Company
	let useDarkMode: Bool
	let address: Address
}

struct Company: Identifiable {
	let id: UUID
	let name: String
}
/// GOOD
/// - Very few properties (4)
/// - No nested entities
/// - Everything define the user (it answers the question "what do I need to create a user?")
struct User: Identifiable {
	let id: Int
	let firstname: String
	let lastname: String

}

struct Address {
  let street: String
	let zipcode: String
  let country: String
}

struct Settings {
  let preferredLanguage: Locale
	let useDarkMode: Bool
}

struct Company {
	let id: Int
	let name: String
}

// Aggregate
struct UserAddress {
	let user: User
	let address: Address
}

// Aggregate
struct UserSettings {
	let user: User
	let address: Address
}

See how we did smaller objects compared to our DTO? This gives us great flexibility in our code about which data we want to give while also making future refactoring simpler and safer.

We also typed locale to use Locale so that we know we have a valid value when using Settings.

Where to use DTO and Domain objects?

While it’s a bad idea to rely on Data model everywhere in your app everyone should use Domain model! Yes I’m talking about your Data layer, your business logic but also your view code!

Let’s see for each one:

  1. Data layer: your DTO being internal you can’t use it as a return value. So best thing to do is to return a Domain object.
  2. Business layer: because all your data layers now expose Domain objects it’s super easy for your business layer to interoperate between all of them and put business rules on it
  3. View layer: I already wrote a little bit about this subject when talking about ViewData in ViewModels and Separation of concern. Your view should definitely use Domain objects! There’s little interest in not doing so apart from… adding complexity.
/// A **User** view so it uses a User object.
/// Would makes little sense to hide user object to a view called... User!
/// Also if you did small Domain object there is nothing heavy in doing so.
struct UserView: View {
	let user: User
}

Time to wrap up

I hope you learned a few things about Data (DTO) and Domain objects. And I hope you’ll embrace starting doing Domain models in your app.

Be assured: it’s a long journey and it takes time to do it properly. But it’s a very useful tool that can greatly improve your code readability and maintenance!

]]>
https://swiftunwrap.com/article/swiftui-rendering-pitfallsSwiftUI rendering pitfallsKnow why your view is not rendering why you think it should have.https://swiftunwrap.com/article/swiftui-rendering-pitfallsTue, 28 Mar 2023 00:00:00 +0000Mastering SwiftUI rendering cycle is not easy and it's common to face issues with it such as having your views rendering too often or not rendering at all while some state changed.

In this article we'll focus on the second issue and see some edge-cases where your view is not rendering while you think it should have.

We'll look to it through the Smart/Dumb components perspective that was explained in Smart and Dumb views in SwiftUI. But note that these rendering issues are related to SwiftUI itself and not the pattern. So even if not using it you might still face them.

Dumb component

As a reminder SwiftUI evaluates a view body when any of its properties change. It checks if any of the parameter you pass to init changed. This is especially true for closures.

Let's take this Dumb component as an example:

struct BookList : View {
    let books: [Book]
    let isBought: (Book) -> Bool
    let buy: (Book) -> Void

    var body: some View {
        List {
            ForEach(books) { book in
                let isBookBought = isBough(book)

                Button(action: { buy(book) }) {
                    Text(isBookBought ? "Purchased" : "Buy")
                }
                .disabled(isBookBaught)
            }
        }

    }
}

It takes as input a list of Book and two closures. Let's use it from another view in two (slightly different) ways:

// 1st
struct LibraryScreen: View {
    @StateObject var viewModel: LibraryViewModel

    var body: some View {
        BookList(books: viewModel.books, isBought: { viewModel.isBought($0) }, buy: viewModel.buy)
    }
}

// 2nd
struct LibraryScreen: View {
    @StateObject var viewModel: LibraryViewModel

    var body: some View {
        BookList(books: viewModel.books, isBought: viewModel.isBought, buy: viewModel.buy)
    }
}

The two codes are similar, the only difference being how isBought is defined:

  • 🥷 through an anonymous closure
  • 🏷️ using a method reference

Only one of these two samples will reflect the new state when user buys a book. Can you guess which one and why? The first one.

Indeed every time SwiftUI evaluates LibraryScreen.body a new anonymous closure is created. Therefore BookList input changed and BookList.body is evaluated again.

In the second example viewModel.isBought is referring to a method. This reference never changes. Because BookList input had no modification BookList.body is not rendered again.

SwiftUI being a closed source framework it is hard to say precisely how it works leading to some mysteries and assumptions 🔮. Since iOS15 you can use Self._printChanges in your body which might help in debugging and understanding when SwiftUI decides to evaluates your body. You can also use profiling tools.

I'm talking about anonymous closures but note this seems to be true for any non Equatable created on the fly 🦋, like Binding(get: { ... }, set: { ... }) for instance.

What about buy then? Even if we use a reference method the view updates 🤯.

This is because buy is an action: we return nothing. We don't rely on any value computed by this function. Therefore using a closure or a reference method has no impact here.

In summary:

  • Use Self._printChanges to debug why a view get re-rendered
  • Be careful when using a reference method returning a value

Smart component

If you read Smart and Dumb views in SwiftUI you know you should not use Smart components other than as root views.

In some very rare cases you might need one though. Unfortunately by doing so you also probably faced the issue of having not the view being updated while the state changed... Did it?

How to know if you need Smart component? If you need to do store some asynchronous work you may need a Smart component. Otherwise you're probably fooling yourself by thinking you need one and can actually just lift the state up!

Let's take an example:

struct ContactView: View {
    let contact: Contact

    var body: some View {
        CallButton(viewModel: CallButtonViewModel(number: contact.number))
    }
}

Here we have a Dumb component (ContactView) using a Smart view (CallButton) which is initialized with the contact number. This is working fine until someone decides to change the contact phone number.

In this case when taping on the button you'll call the contact... using its old phone number 😳.

This actually makes sense. @State(Object) are restored with their initial value on every rendering cycle. So our view ViewModel is always restored to its very first instance. This is how we can have immutable structs (ContactView) and yet being stateful.

rendering lifecycle

To avoid the state to be restored we need to reset it by stating to SwiftUI that our CallButton identity changed. In order to do so we can rely on View.id(_:):

struct ContactView: View {
    let contact: Contact

    var body: some View {
        CallButton(viewModel: CallButtonViewModel(number: contact.number))
            .id(contact.number)
    }
}

Now whenever our contact number changes the whole button state will be reseted.

]]>
https://swiftunwrap.com/article/you-dont-need-protocolsYou don't need protocolsLet's see why sometimes protocols are not the best option by sharing with you a few alternatives.https://swiftunwrap.com/article/you-dont-need-protocolsTue, 14 Feb 2023 00:00:00 +0000After discussing with developers I realized some of theme use what I would call "protocol first" approach: make a protocol then think about the need.

While it might works sometimes it comes with downsides: protocols are not to be used everywhere! They are good tools but not the only one you should use. Just like you don't use a hammer 🔨 for everything... (except maybe to to make Xcode 🛠 working, but that's another topic!).

Today I'd like to show you a few alternatives you can use instead of protocols: closures, structs and enums.

Closures

Let's say you need to a method to open iOS settings ⚙️ from your app. This is something that would be mostly done using your AppDelegate. But because you don't want to tie your classes to it you decide to... define a protocol!

protocol SettingsProvider {
    func openSettings() -> Void
}

extension AppDelegate: SettingsProvider {
    func openSettings() -> Void {
        ...
    }
}

class FooViewModel {
    init(SettingsProvider: SettingsProvider) {

    }
}

The code is working and technically there is nothing wrong. However see how we created a protocol with only one method? Beside this protocol will never grow: it will always have only one method requirement.

If we look to testing a we also notice we need to provide a concrete instance type:

class SettingsProviderMock: SettingsProvider {
    ...
}

So much code for yet such a little feature (opening iOS settings). Maybe we could go simpler? Yes we can! 💪

By using closures we can effectively get the same result with less code:

class FooViewModel {
    init(openSettings: @escaping () -> Void) {
        ...
    }
}

That's all we need. See how we even got more clarity? Instead of referring to a (little bit) obscure SettingsProvider we now clearly state we intend to open the settings.

What about testing? Here again we gain in simplicity and flexibility. We now just need to provide our closure when writing our test. Which can do nothing or trigger some XCTAssert*.

class FooTests: XCTestCase {
    func test_XX() {
        let viewModel = FooViewModel(openSettings: { ... })
    }
}

In summary if you have a small isolated requirement then consider using closures before going for the mighty 🪄 protocol.

Structs

Structs are under-used by some Swift developers. Let's say we have an app working with a User. This is a very typical problem that you'll encounter in 99,9% apps.

This user might come from multiple sources like Apollo (GraphQL) or CoreData database. So in order to abstract its usage you decide to use a protocol:

protocol UserModel {
    let id: UUIDget }
    let firstName: Stringget }
    let lastName: Stringget }
}

first glance there’s nothing wrong with this approach, just like for our previous example. However as soon as you start using it you may facing some issues:

  1. 🐣 You cannot create a UserModel instance. You always have to rely on someone else to do so. Very cumbersome!
  2. 🗝 All your implementations need to conform to this specific contract. You could argue this is the objective...
  3. 😓 ...But you soon realize you have have hard-time having both GraphQL (Apollo) and CoreData models implementing the same protocol. Indeed most CoreData attributes are optional!

One way to fix it would by declaring both attribute storage and protocol conformance:

class UserCoreData: NSManagedObject {
    @NSManaged private var _firstName: String?

    var firstName: String { _firstName ?? "" }
}

However it becomes unclear what's the difference _firstName and firstName.

Finally we also need to create a specific mock implementation for our testing:

struct UserModelMock: UserModel {
    let id: UUID
    let firstName: String
    let lastName: String
}

Just... a plain data struct 😳. Isn't it a little bit weird to have such a type only for testing?

If we look to both our struct and protocols we can see they are identical. That is because our protocols is composed only of attributes. More importantly: there is no logic to implement.

It may now becomes obvious that the best approach to represent such requirement is by using a struct:

struct UserModel {
    let id: UUID
    let firstName: String
    let lastName: String
}

Doing this we already fix two of our issues:

  1. 🐣 We can manually create a UserModel instance
  2. 🧪 We can write tests using this implementation

As for handling data from multiple sources the best approach is by converting source data to our model:

extension UserCoreData {
    func toModel() -> UserModel {
        UserModel(id: id, firstName: firstName ?? "", lastName: lastName ?? "")
    }
}

Doing so you gain more flexibility:

  • You can handle each data source specificity by having fallback values... or throwing errors if you want to
  • You effectively hide your data source implementation
  • You make it more explicit when working with the data source and when using your model

Enums

Let's take an example where you need to handle multiple user types inside your app:

  • a regular user, like the one we just saw
  • an admin users, who have additional permission
  • an anonymous users, who has no identity and more restricted access

Trying to modify our previous struct we may end up with something like this:

struct UserModel {
    var id: String? // optional because anonymous user has no id
    var firstName: String
    var lastName: String
    var isAdmin: Bool
}

However we now have to handle optional id everywhere in our code. We can now also create weird states like UserModel(id: nil, firstName: "", lastName: "", isAdmin: true) where user is... anonymous but admin at the same time 🤯.

One might argue that if we stood using protocols this issue would not have happend for sure!

protocol AnonymousUser {

}

protocol User: AnonymousUser {
    var id: String
    var firstName: String
    var lastName: String
}

protocol AdminUser: User { }

Indeed it solves our issues. But it comes with its own downsides:

  • We have a naming issue: User is an AnonymousUser... that's kind of strange 🤔
  • This time we need not 1 but 3 struct mocks for writing tests!

Beside what if we wanted to have an attribute specific to anonymous users?

Let think about we need. Our user can either be anonyous or a regular user or an admin. Most of the time when using "or" it means we're looking for an enum:

enum UserGrant {
    case anonynmous
    case regular(User)
    case admin(User)
}

Now we can easily check if our user is anonymous, admin or just a plain regular one. One interesting thing to note is that this information is not linked to User itself: these are two different objects.

As a result any method dealing with User but don't requiring its grant can continue using User. This makes our code less tighted to unrelated concept.

Conclusion

Protocols are great tools but not the only one available in Swift. As counter-intuitive as it may sounds to some readers other solutions can result in simpler and more maintainable code.

As such don't fall into "protocol first" paradigm and look to some of Swift most powerful features: struct, enums and closures.

]]>
https://swiftunwrap.com/article/dependency-containerDependency Injection ContainerLet's have a look on how to DI Container can help us injecting our dependencies.https://swiftunwrap.com/article/dependency-containerTue, 3 Jan 2023 00:00:00 +0000In a previous article Dependency Injection principles I explained what is dependency injection 💉. Let's now have a look on how to manage and use dependencies through a Dependency Container.

Is a Container mandatory?

Let's be clear: you don't need a Dependency Injection Container to benefit from Dependency Injection. However DI Container can be really helpful when dealing with multiple dependencies, especially to manage their lifecycle.

In essence a DI Container is a type knowing how to build an object and all its dependencies. You can write your own or use a library. We'll do both but let's start by doing our own.

If you did not read Dependency Injection principles or are not very familiar with Dependency Injection concepts then read it first! Using DI Container upon 💩 will just bring you bigger 💩💩.

Creating a DI Container

Let's reuse our MovieViewModel and MovieRepository from Dependency Injection principles and create a Dependency Container for it.

class DependencyContainer {
    func resolveMovieViewModel() -> MovieViewModel {
        MovieViewModel(repository: resolveMovieRepository())
    }

    private func resolveRepository() -> Repository {
        Repository(urlSession: .shared, jsonDecoder: JSONDecoder())
    }
}

As you can see the code is actually fairly identical to what we had before. The difference being the resolution now happens in a dedicated class rather than relying on parameters default value.

One advantage of this approach is that you cannot forget to resolve a dependency: in order to instantiate a MovieViewModel you know need to explicitly give a MoveRepository.

Lifecycle

One neat thing about Dependency Container is how you can configure objects lifecycle.

Here for instance we can quickly swipe our Repository from a created-at-every-call to a shared one (a singleton per se).

class DependencyContainer {
    private lazy var repository = Repository(urlSession: .shared, jsonDecoder: JSONDecoder())
    
    func resolveMovieViewModel() -> MovieViewModel {
        MovieViewModel(repository: resolveRepository())
    }

    /// now we'll always get the same repository instance
    private func resolveRepository() -> Repository {
        repository
    }
}

Swinject

Swinject 💉 is one of the most popular Dependency injection framework for Swift.

Our previous container could be rewritten as follow:

import Swinject

class AppAssembly: Assembly {
    func assemble(container: Container) {
        container.register(MovieViewModel.self) { resolver in
            MovieViewModel(repository: resolver.resolve(Repository.self)!)
        }

        container.register(Repository.self) {
            Repository(urlSession: .shared, jsonDecoder: JSONDecoder())
        }
        .inObjectScope(.container) // singleton
    }
}

As you can see the code is fairly the same.

Using the container

The most important thing to remember is that Dependency Container manages dependencies lifecycle ♾️. Therefore it's very important to have only one container instance.

One approach to ensure this uniqueness is by instantiating it in AppDelegate.

import Swinject

class AppDelegate {
    private let depencenyResolver = Assembler([AppAssembly()]).resolver
}

How to use it then depends on whether or not you have a router. Let's start with the least probable case 😝: you have a router inside your app.

Routed app

Router pattern 🛣 is great because it's a common path where you main app logic always goes through.

Magellan is one such example: it has a Router closure called every time we display a new screen. For people using SwiftUI, NavigationStack and NavigationPath can be used to build such a component.

In this approach our router is in charge of resolving dependencies:

import Magellan

class AppDelegate {
    private let dependencyResolver = Assembler([AppAssembly()]).resolver
    private var navigation: Navigation!

    func application(_ application: UIApplication, didFinishLaunchingWithOptions...) -> Bool {
        setupNavigation()
    }

    func setupNavigation() {
        navigation = Navigation(root: window.rootViewController!) { [dependencyResolver] route, _ in
            switch route {
            case .movies:
                let viewController = StoryboardScene.Main.movies.instantiate()
                viewController.viewModel = MoviesViewModel(
                    repository: dependencyResolver.resolve(MovieRepository.self)!
                )

                return Route(viewController).present(PageSheetPresentation())
        }
    }
}

If you don't have a Router don't worry though! You can still use a dependency container.

The less good but more usual case

With no router you need to access your Dependency Container from every root controllers (or views).

In SwiftUI you can rely on @EnvironmentObject to access and use you container. However be sure to not over use it and access it only from root views (basically your smart views if you've been following Smart/Dumb approach).

import Swinject

struct MyScreen: View {
    @EnvironmentObject dependencyResolver: Resolver

    var body: some View {
        NavigationLink() {
            ....
        }
    }

    @ViewBuilder
    private var moviesScreen: some View {
        let viewModel = MoviesViewModel(repository: dependencyResolver.resolve(MovieRepository.self)!
        
        MovieScreen(viewModel: viewModel)) {
            ...
        }
    }
}

On UIKit you'll have to set the instance on each root view controller.

import Swinject

class RootController: UIViewController {
    var dependencyResolver: Resolver!

    func showMovies() {
        let viewController = StoryboardScene.Main.movies.instantiate()
        
        viewController.dependencyResolver = dependencyResolver
        viewController.viewModel = MoviesViewModel(
            repository: dependencyResolver.resolve(MovieRepository.self)!
        )

        pushViewController(viewController, animated: true)
    }
}

This way you'll be able to access your container and resolve your dependencies.

This is how you can either build your own DI Container or use a third-party library. Which approach to use is mostly a matter of taste although I would advise for libraries as it can greatly simplify object lifecycle.

However libraries might come with a very big tradeoff you probably didn't notice at first glance: compile-time safety 👷! We'll dive 🏊‍♂️ on this in a future article.

]]>
https://swiftunwrap.com/article/create-feedback-componentCreating a feedback componentHow to leverage SwiftUI and Combine capabilities to build a feedback UI component.https://swiftunwrap.com/article/create-feedback-componentTue, 15 Nov 2022 00:00:00 +0000It is a good practice to provide a visual feedback to user whenever he/she performs an action, whether to inform the operation was performed successfully or an error happened.

I saw a few systems in past projects I worked on but most of them not only required lot of boilerplate code but also tend to expose UI information into business layers such as a ViewModel.

As it is very important to keep a Separation of Concern let's see another way of doing it! We'll do it step by step:

  1. First by analysing our needs
  2. Then by building a dumb message UI
  3. Then by adding logic show it locally in a view
  4. Finally by making it available to the whole application

Analysing

Before diving into code it is a good thing to first analyse a little bit what we want to achieve.

Our goal is to display messages whenever user perform actions such as when rating a movie.

This is what we know for sure:

  • We use an ObservableObject as storage
  • We store domain only attributes inside it
  • We want to compute our feedback UI messages from our domain attributes

In other words giving a state:

class GameViewModel: ObservableObject {
    @Published var lastUserRating: Int = 0
    @Published var error: Error?
}

I want to be able to display "Rate updated! Thank you" or "An error occurred". Meaning whenever $lastUserRating change we'll display "Rate updated!" while showing "An error occurred" when $error send a new value.

Sometimes we may also use Result instead (to avoid being in success and failure at the same time) but end goal should be the same:

class GameViewModel: ObservableObject {
    @Published var lastUserRating: Result<Int, Error>?
}

So we'll try to build a component that

  1. Display a message when a publisher changes
  2. Determine whether the message should be success or error depending on the type
  3. Deal with both Result and other types

Feedback UI

Let's start by creating a dumb view to displaying a feedback. Its only role is to:

  • Show a text
  • Add style to the overall text/feedback (font, color, etc...)
/// Decides which kind of message we display (a success or an error)
enum FeedbackType {
  case success
  case error
}

struct FeedbackView: View {
  let message: LocalizedStringKey
  let type: FeedbackType

  private var backgroundColor: Color {
    switch type {
    case .success:
      return .green
    case .error:
      return .red
    }
  }

  var body: some View {
    Text(message)
      .padding()
      .background(backgroundColor)
      .foregroundColor(.white)
      .cornerRadius(8)
      .frame(maxWidth: .infinity, minHeight: 40)
      .padding(.horizontal, 16)
  }
}

Nothing too fancy here except we can display a success/error message. Now let's see how to effectively use it when receiving a value from a Publisher.

Local feedback

You may wonder why local first? Because we're building block by block 🧱. No need to rush on displaying feedback on global application if we can't even display it on a single screen!

Let's create a view modifier (FeedbackModifier) listening to a publisher and displaying an overlay feedback on a view:

  1. The message will be provided by the developer through a closure
  2. We also introduce a Feedback alias to simplify the closure type
typealias Feedback = (message: LocalizedStringKey, type: FeedbackType)

struct FeedbackModifier<P: Publisher>: ViewModifier where P.Failure == Never {
  /// the publisher sending values over time
  let publisher: P
  /// the feedback message to display to the user
  let message: (P.Output) -> Feedback?

  /// the actual feedback value we'll display to the user
  @State private var feedback: Feedback?

  func body(content: Content) -> some View {
    content
      .overlay(alignment: .top) {
        if let feedback {
          FeedbackView(message: feedback.message, type: feedback.type)
        }
      }
      .onReceive(publisher) {
        feedback = message($0)
      }
  }
}

Let me explain you what we did:

  • We listen to the publisher changes using onReceive
  • Every time we receive a new value, we update our internal state feedback
  • When feedback changes, the view is redrawn (thanks to @State) and we display it if not nil

Our code works with any kind of publisher but is not very practical for the cases we listed at the beginning of the article. Let's add some helper to View to deal with those cases:

extension View {
  /// General helper: just forward to FeedbackModifier
  func sendFeedback<P: Publisher>(
    publisher: P,
    message: @escaping (P.Output) -> Feedback?
  ) -> some View where P.Failure == Never {
    modifier(FeedbackModifier(publisher: publisher, message: message))
  }

  /// Show an error message when Publisher output is Error
  func sendFeedback<P: Publisher>(publisher: P) -> some View where P.Output: Error, P.Failure == Never {
    sendFeedback(publisher: publisher) { error in
      switch error {
      case is LocalizedError:
        return (message: LocalizedStringKey(error.localizedDescription), type: .error)
      default:
        return (message: "error_default", type: .error)
      }
    }
  }

  /// Show a success message for a Publisher output
  func sendFeedback<P: Publisher>(
    publisher: P,
    message: @escaping (P.Output) -> LocalizedStringKey?
  ) -> some View where P.Failure == Never {
    sendFeedback(publisher: publisher) { message($0).map { (message: $0, type: .success) } }
  }

  /// Show a message when Publisher is Result
  func sendFeedback<P: Publisher, Success, Failure: Error>(
    publisher: P,
    message: @escaping (Success) -> LocalizedStringKey?
  ) -> some View where P.Output == Result<Success, Failure>, P.Failure == Never {

    sendFeedback(publisher: publisher) {
      switch $0 {
      case let .success(output):
        return message(output).map { (message: $0, type: .success) }
      case let .failure(error) where error is LocalizedError:
        return (message: LocalizedStringKey(error.localizedDescription), type: .error)
      case .failure:
        return (message: "error_default", type: .error)
      }
    }
  }
}

We can now use them into our code and see the result:

struct GameView: View {
    @StateObject var viewModel: GameViewModel

    var body: some View {
      ...
      .sendFeedback(publisher: viewModel.$lastUserRating) { _ in "Thanks for your rating!" }
      .sendFeedback(error: viewModel.$error)
    }
}

As you can see it's working but not perfectly: last modifier wins over the previous one. This is because both method (sendFeedback) manage their own state. Let's fix this by lifting state up.

Application-wide feedback

Building an application-wide feedback system requires some container mechanism like how NavigationView and NavigationLink work: whenever a feedback is sent from child component the container will display it.

Therefore we'll need two components:

  1. FeedbackContainerModifier as the container listening to feedback
  2. FeedbackSenderModifier sending the feedback

To communicate between these two we will still use a publisher but stored in EnvironmentObject. However EnvironmentObject works only with concrete types: we cannot just say "give me a Publisher".

So we'll create a wrapper, FeedbackNotifier, which will hold our feedback value and use it instead.

FeedbackSenderModifier

This modifier does only one thing: listening to a publisher to forward its value to FeedbackNotifier:

@MainActor
class FeedbackNotifier: ObservableObject {
  @Published var feedback: Feedback?
}

struct FeedbackSenderModifier<P: Publisher>: ViewModifier where P.Failure == Never {
  let publisher: P
  let message: (P.Output) -> Feedback?

  @EnvironmentObject var notifier: FeedbackNotifier

  func body(content: Content) -> some View {
    content
      .onReceive(publisher) { output in
        guard let feedback = message(output) else {
          return
        }

        notifier.feedback = feedback
      }
  }
}

FeedbackModifier

FeedbackContainerModifier is actually FeedbackModifier renamed and with a small change:

struct FeedbackContainerModifier: ViewModifier {
  @StateObject private var notifier = FeedbackNotifier()

  @State private var feedback: Feedback?

  func body(content: Content) -> some View {
    content
      .environmentObject(notifier)
      .overlay(alignment: .top) {
        if let feedback = feedback {
          FeedbackView(message: feedback.message, type: feedback.type)
        }
      }
      .onReceive(notifier.$feedback) { newFeedback in
        feedback = newFeedback
      }
  }
}

Instead of listening to a publisher we now listen to FeedbackNotifier. The object is also set as EnvironmentObject so that FeedbackSenderModifier can access it.

Doing this we can test again our code.

extension View {
  func feedbackContainer() -> some View {
    modifier(FeedbackContainerModifier())
  }
}

struct MyApp: App {
  var body: some View {
    ...
      .feedbackContainer()
  }
}

This time one feedback is displayed at a time replacing the previous one 💪. Only one thing missing: dismissing the feedback.

Auto-dismiss

Dismissing our view after a few seconds can be done using:

  1. 🔀 DispatchQueue
  2. Timer

DispatchQueue is easy to use but it's a "fire and forget" solution: once planned you cannot cancel it.

On our side we want to cancel and plan again our dismiss if publisher changes in the meantime. So we'll rely on Timer instead.

struct FeedbackContainerModifier: ViewModifier {
  @StateObject private var notifier = FeedbackNotifier()

  @State private var timer = Timer.publish(every: 4, on: .main, in: .common)
  @State private var feedback: Feedback?
  @State private var cancellable: Cancellable?

  func body(content: Content) -> some View {
    content
      .environmentObject(notifier)
      .overlay(alignment: .top) {
        if let feedback = feedback {
          FeedbackView(message: feedback.message, type: feedback.type)
        }
      }
      .onReceive(notifier.$feedback) { newFeedback in
        timer = Timer.publish(every: 4, on: .main, in: .common)
        cancellable = timer.connect()

        feedback = newFeedback
      }
      .onReceive(timer) { _ in
        feedback = nil
        cancellable?.cancel()
      }
  }
}

Here's the changes we did:

  • Whenever publisher changes we connect to timer
  • When timer fires (here after 4 seconds) we set feedback back to nil (and clear our timer), effectively hiding it

Caveat: modals

The code is working great but there is one case where it doesn't play as expected: modals.

When showing a modal and trying to display a feedback the later actually displays behind the modal. This is due to view hierarchy: our FeedbackContainerModifier is attached to the presenting view.

There are two ways of resolving this issue:

  1. First one is to add a new .feedback() on the presented view. This is simple and working well in most cases. However you need to remember doing it.
  1. Second is to move feedback into a dedicated window. This is the best solution however it requires falling back to UIKit and adding a bunch of code into your AppDelegate.

Conclusion

With very lines of code we now have a fully functional component providing feedback to user.

Last but not least we can rely on some Combine powerful operators to achieve some great behaviour!

You can find the code along a sample project on Github. Enjoy!

]]>
https://swiftunwrap.com/article/development-workflowDevelopment workflowsHow Trunk based development can help teams in being efficient.https://swiftunwrap.com/article/development-workflowTue, 27 Sep 2022 00:00:00 +0000A few weeks ago a few people (well... just one person 😄) asked me how I was handling release process.

To give a complete answer I actually described how my team is working together and thus our development workflow: how do we make branches, merge requests, and so on.

I thought it might be of interest to some people. So here it is!

GitFlow is dead 💀

Like most people I guess I started using using GitFlow approach. While working with my colleagues I always found it cumbersome and error prone.

Then came a day where I worked a single iOS software engineer on a project. I quickly ditched some of Gitflow processes like creating release branches (as I was the only committer) then step by step... just throwing everything away.

Because I really appreciated this approach I tried to also apply it when working as a team. Disclaimer: it work 🎉.

I later found out I did not invent anything new 🥲. Some people already had the same idea and called it trunk based development

GitFlow example: 3 feature branches merged when completed into develop

Trunk based development

Trunk based development is described as a model where "developers collaborate on code in a single branch called ‘trunk’ (or main/master), resist any pressure to create other long-lived development branches by employing documented techniques. They therefore avoid merge hell, do not break the build, and live happily ever after". Let's break it done.

One branch

Unlike GitFlow every developer commit its change to the main branch. Of course those changes are not made without prior approval so you still make merge requests. However here MR/branches are short-living (a few days) to be merged as quickly as possible on the main branch.

No long living branch

As branches are short living you need to divide features into small working branches, each one adding new functionalities. For instance:

  • 🗺 One branch adding the domain model and webservices
  • 🎨 One creating the UI
  • 🔌 Another one to plug it
  • ♻️ Another one refactoring some deprecated code, and so on...

The technique of MR containing unplugged code until another one plug everything is a good strategy to merge finished code but uncompleted feature.

This approach not only to merge quickly but also help having small MR meaning faster (and more accurate) review leading to MRs being merged faster 🎉.

This may not prevent you from delivering uncompleted feature though. To avoid exposing it to users you can rely on another technique: feature flags 🏳.

It can be challenging for people not used to this way of coding as it require approach development a different way. But it really pays off.

Same workflow example using master/main branch: branches are smaller and merged into main

QA Testing 🧐

Some might wonder how QA can test in such environment. Two possibilities:

  1. You merge your branch once validated by dev and QA testers
  2. You merge once validated by dev and deploy a build from main branch for QA to test

I prefer going with the latest approach for multiple reasons:

  1. Merge requests are merged more quickly
  2. Developers need to be more attentive as they know their work will be merged before QA tests
  3. I believe QA provide additional tests to find some edge cases but a team should be able to deliver confidently even with no QA

End result is that QA indeed find bugs but very few and very rarely blocking ones.

Release time ⌛️

Once ready how can you make a release? Very simply actually:

git tag -a v5.0.0 -m "Some release notes"

This command create a tag on Git repository flagging the commit as being the release commit. One created our CI detect it, run a build and upload it on Testflight. And... that's all 🤷‍♂️. Easy, simple to remind process.

This has so much advantages compared to maintaining a not-so-useful branch like on Gitflow:

  • It's easy to create
  • It's visible in Git history
  • Tag description can be used as a changelog
  • Using git diff v4.0.0...5.0.0 you can quickly see which commits were made since previous release

Sometimes though process can be a little bit longer: last minute fix before release or event production hotfix. How to handle those cases? By fetching and starting back from the tag:

git checkout v5.0.0
git checkout -b release/5.0.1

In this kind of scenario yes we create a release branch. Only for the time of patching the release and sending it in production.

We even sometimes drop the release branch creation if our main branch was not modified since last release. But let's be honest it's very rare unless when spotting last minute bugs.

How do we make sure though that everything we send as hotfix also end up in main?

Same player merge again ‼️

Trunk based development is pretty clear on that:

  1. You first create a MR fixing main
  2. You then create a second one applying the patch on the target release

In theory it work well. In practice it is sometimes annoying as the patch might need some modifications between main and release.

Also once the fix is made on main we very often... forgot to apply the fix on the release branch 🙈. So we tried the other way around but the problem remain as we also forget to apply the fix on main 😅.

So for now we don't have any perfect solution to this (small) problem 🙃.

That's a wrap

I cannot tell you how pleasant it is to work using Trunk based development. Processes are simple to follow/remember and things are going quick.

If you're still following Gitflow I would definitely advise you to give a try. It may require you some adoption time but it's definitely worth it.

Spend more time coding and less resolving merge conflicts! ⚔️

]]>
https://swiftunwrap.com/article/making-formMaking a formHow to build a form in a way you probably never did it before: from Domain perspective instead of UI.https://swiftunwrap.com/article/making-formTue, 26 Jul 2022 00:00:00 +0000Suppose we want to create a contact from our app. To do so we introduce a form that the user can fill out in order to generate a contact.

Traditional way for building the form would be throwing all fields in the view along their bindings in the object handling the view logic (like a ViewModel):

class ContactViewModel: ObservableObject {

  @Published var firstName = ""
  @Published var lastName = ""

  @Published var error: Error?
  @Published var contact: Contact?

  func save() {
      guard !firstName.isEmpty, !lastName.isEmpty else {
        error = ContactError.missingMandatoryFields
      }

      contact = Contact(firstName: firstName, lastName: lastName)
  }
}

It work for very simple case. But complexity can grow very quickly:

  • 🖊 How do you track the form is modified?
  • 🙋‍♀️ How can you fill the form with data from one contact?
  • 🪆 How do you handle nested forms? For example a contact having multiple phone numbers requiring validation?

Another issue might rise when ContactViewModel need to handle other complex cases not directly related to the form: for instance checking whether a contact already exist based on its phone number.

In this context it become clear something is wrong:

  • 🦛 The ViewModel will grow fast
  • 🤯 It is hard distinguishing what is about form filling and what is not.

To help us with all these things let's tackle the problem from another perspective: Domain.

The Domain approach

Instead of thinking about UI let's just think about what we want to achieve: being able to create a Contact by filling its properties one by one.

To do that let's introduce a new object whose sole purpose will be to build the contact with no UI in consideration: ContactBuilder. It will be our form Domain representation.

Building an object

As said before the builder is responsible for creating a valid Contact. So let's just add that:

struct ContactBuilder {
  func build() throws -> Contact {
    throw ContactBuilderError.missingMandatoryFields
  }
}

Having no way for building a valid contact for now it will just throw an error. A few things compared to the previous example:

  1. We have only one way of building our model (build method) and its content is now an implementation detail
  2. thrown error is coming from the builder itself (ContactBuilderError). This is important because these errors are specific to our builder, not to our result model
  3. We have either an error or a valid contact. Compare to before where we could have both at the same time

Properties

Next step is to add contact attributes by declaring their equivalent into the builder. Let's add firstName and lastName and use the builder in the ViewModel:

struct ContactBuilder {
  var firstName = ""
  var lastName = ""

  func build() throws -> Contact {
    guard !firstName.isEmpty && !lastName.isEmpty else {
      throw ContactBuilderError.missingMandatoryFields
    }

    return Contact(firstName: firstName, lastName: lastName)
  }
}
class ContactViewModel: ObservableObject {
  @Published var builder = ContactBuilder() {
    didSet { contact = Result(builder.build()) }
  }

  @Published var contact: Result<Contact, Error>?
}

You might notice how our form builder is now a unique entity: making multiple changes at once will trigger only one update on the ViewModel.

Adding properties canSave or isModified is also now very easy:

class ContactViewModel {
  @Published var builder = ContactBuilder() {
    didSet {
      contact = Result(builder.build())
      isModified = true
    }
  }

  @Published var isModified = false

  var canSave: Bool { (try? contact.get() != nil) ?? false }
}

We can even go further and check if content was actually modified by making ContactBuilder conforming to Equatable:

struct ContactBuilder: Equatable {
  // easy peasy 🍋
  var isModified: Bool {
    builder != ContactBuilder()
  }
}

This give you lot of flexibility to implement things as you need based on your functional rules.

Nested form

Previous example focused on creating a simple object (a contact) but sometimes you need to go further and create additional objects along it.

In this scenario your form quickly contain dozen of fields. Following the traditional way it would just become a nightmare 🤯. This is where the form builder approach shine 💪

Let's continue with our contact and add the ability to set multiple phone numbers composed of two fields: a country and the number itself. Because it is a separate entity the best thing is to create a dedicated builder:

struct PhoneNumberBuilder {
  var country = Country.us
  var number = "+1"

  func build() throws PhoneNumber {
    // again, implementation detail
  }
}

Now all we need to do is to add a PhoneNumberBuilder in ContactBuilder and... that's it 🤷‍♂️:

struct ContactBuilder {
    var phoneNumbers: [PhoneNumberBuilder] = []

    func build() throws Contact {
      ...
      let phoneNumbers = try self.phoneNumbers.maptry $0.build() }

      return Contact(firstName: firstName, lastName: lastName, phoneNumbers: phoneNumbers)
    }
}

Just the same way we can make a View builder for phone number and use it in our parent view:

struct PhoneNumberForm: View {
  @Binding builder: PhoneNumberBuilder

  var body: some View {
    TextField("Country", text: $builder.country)
    TextField("Number", text: $builder.number)
  }
}

struct EditContactScreen: View {
  @StateObject var viewModel: ContactViewModel

  var body: some View {
    ...
    List($viewModel.builder.phoneNumbers) { $phoneNumber in
      PhoneNumberForm(builder: $phoneNumber)
    }
  }
}

This code won't compile though as List require each item to be Identifiable. We can fix it by using number as PhoneNumberBuilder id:

extension PhoneNumberBuilder: Identifiable {
  var id: String { number }
}

But while the code now compile we entered a dangerous zone: duplicated id. Nothing prevent us from having the same number twice!

As you probably already know this is a bad idea to rely on potential unique id like phone/social numbers. Instead we'll use a generated identifier which is a perfect solution for builders not having a provided id:

struct PhoneNumberBuilder: Identifiable {
  let id = UUID()
}

Conclusion

By focusing on domain rather on UI we now have a testable form builder. And by using nested builders we are now able to easily add new fields without increasing the complexity.

]]>
https://swiftunwrap.com/article/formatting-data-uiFormatting in UIAn easy way to share code across views and test your UIhttps://swiftunwrap.com/article/formatting-data-uiTue, 14 Jun 2022 00:00:00 +0000Writing UI often come with the need of formatting data to display it to the user.

We usually rely on formatters provided by Apple to do so such as DateFormatter and now Date.formatted. However they are unable to format application dedicated objects like contacts, books, users or players for instance.

In these situations it become clear we need to implement our own logic to handle this cases. But how?

Let's start by having a look on the most common way of solving this problem and how using formatter instead can simplify our coder life.

The classic way: ViewData 🎻

Back in ViewModel and Separation of Concern I presented the ViewData approach. That's the classic way 🎻.

Let's reuse the example from this article:

class BookViewData {
    private let book: Book
    private let formatter = DateFormatter()

    var title: String { book.title }
    var publishedAt: String { formatter.string(from: book.publishedAt)! }
}

Let's now imagine a more complex scenario where publishedAt need some tweaking and title is actually a concatenation of two attributes:

class BookViewData {
  private let book: Book
  private lazy var formatter: DateFormatter = {
    let formatter = DateFormatter()

    formatter.setLocalizedDateFormatFromTemplate("EEEE, MMM d, yyyy")

    return formatter
  }()

  var title: String {
    [book.title.capizalized, book.subtitle.capitalized].joined(separator: " - ")
  }
  var publishedAt: String { formatter.string(from: book.publishedAt)! }
}

This is perfect valid code and not too complex. It's even testable ✅. Is it? 🧐

Testing time

class BookViewDataTests: XCTestCase {
  func test_publishedAt_returnFormattedDate() {
    let viewData = BookViewData(book: ...)

    XCTAssertEqual(viewData.publishedAt, "2022 June 7th")
  }
}

On my French computer this test will fail 💥. Why? Because we're implicitly relying on DateFormatter.locale and not enforcing it during testing. So our tests will fail on computer where the locale do not match the implicit one.

All your data are belong to us

Let's now say we need to display our books as a list. Obviously we'll need to show the title. How should we do that?

One might create a BookRowViewData tailored for row displaying. ViewData are supposed to be tailored to the view they are used in so it would make sense:

class BookRowViewData {
  private let book: Book
  private lazy var formatter: DateFormatter = {
    let formatter = DateFormatter()

    formatter.setLocalizedDateFormatFromTemplate("MMMM yyyy")

    return formatter
  }()

  var title: String {
    [book.title.capizalized, book.subtitle.capitalized].joined(separator: " - ")
  }
  var publishedAt: String { formatter.string(from: book.publishedAt)! }
}

But while working it occur a lot of code duplication. On the other hand it allow us to configure each attribute slightly differently like publishedAt using a different format.

To avoid duplication we could put everything in one single BookViewData and sharing it across views:

class BookViewData {
  private let book: Book
  private lazy var dayFormatter: DateFormatter = {
    let formatter = DateFormatter()

    formatter.setLocalizedDateFormatFromTemplate("EEEE, MMM d, yyyy")

    return formatter
  }()

  private lazy var monthFormatter: DateFormatter = {
    let formatter = DateFormatter()

    formatter.setLocalizedDateFormatFromTemplate("MMMM yyyy")

    return formatter
  }()

  var title: String {
    [book.title.capizalized, book.subtitle.capitalized].joined(separator: " - ")
  }
  var publishedAtDay: String { dayFormatter.string(from: book.publishedAt)! }
  var publishedAtMonth: String { month.string(from: book.publishedAt)! }
}

However now the view and its ViewData are not tighed together as we said just before. As such we don't know which attributes are used by a view.

This indicate one thing 🕵️‍♀️: formatting is not so much related to a view rather than a model. So instead let's transform our Data class into a Formatter.

The magic way: Formatters 🧙‍♂️

We'll first start by creating a date formatter tailored to our app:

class DateFormatter {
  enum Format: String {
    case byDay = "EEEE, MMM d, yyyy"
    case byMonth = "MMMM yyyy"
  }

  static func string(_ date: Date, to format: Format) -> String {
    let formatter = DateFormatter()

    formatter.setLocalizedDateFormatFromTemplate(format.rawValue)

    return formatter.string(from: date)
  }
}

By doing a custom date formatter we can now display our book date by day or month depending on the screen. But most important the logic can now be reused for any date in our app 💪.

What about title? This is something very specific to book so we'll make a BookFormatter to handle this case:

class BookFormatter {
  static func string(title book: Book) -> String {
    [book.title.capizalized, book.subtitle.capitalized].joined(separator: " - ")
  }
}

While very similar to BookViewData our BookFormatter is decoupled from unrelated formatters (date). Also by using static any BookFormatter function is now independent one from another. What about testing?

Testing

Let's reuse our previous test and rewrite it using our new formatter:

class dateFormatterTestsTests: XCTestCase {
  func test_string_byDay_returnStringWithDay() {
    XCTAssertEqual(DateFormatter.string(Date(), format: .byDay), "2022 June 7th")
  }
}

That test still won't work on my computer 💥😬

To fix it we'll define the locale to use when formatting and set it in our test:

class DateFormatter {
  static func string(_ date: Date, to format: Format, locale: Locale = .current) -> String {
    let formatter = DateFormatter()

    formatter.locale = locale
    formatter.setLocalizedDateFormatFromTemplate(format.rawValue)

    return formatter.string(from: date)
  }
}

class dateFormatterTestsTests: XCTestCase {
  func test_string_byDay_returnStringWithDay() {
    XCTAssertEqual(DateFormatter.string(Date(), format: .byDay, locale: Locale(identifier: "en_US")), "2022 June 7th")
  }
}

And now everything work 💪.

Composition

We saw some basic formatters usage. In most apps however you might have more complex use cases.

For instance in my current work we need to display a participant: it can be either a phone number or a app user. However we also need to be able to explicitly display a user or a phone number.

How did we handle that? By making 3 formatters: one being just a composition of the two others.

class UserFormatter {
  static func string(_ user: User) -> String {
    // format the user, possibly with something like PersonNameComponentsFormatter
  }
}

class PhoneNumberFormatter {
  /// display a phone number in a e164 human readable way. Example: +33 1 02 03 04 05
  static func string(_ numbr: PhoneNumber) -> String {
  }
}


class ParticipantFormatter {
  static func string(_ participant: Participant) -> String {
    switch participant {
      case .user(let user):
        return UserFormatter.string(user)
      case .number(let phoneNumber):
        return PhoneNumberFormatter(phoneNumber)
    }
  }
}

Thanks to this approach we now have a testable code and no more formatting duplication code 🚀.

Format style

Starting with iOS 15 you can use formatted on some elements like Date which can save you time by not needing to make a formatter.

Under the hood Date.formatted use a new protocol: FormatStyle. You can use it to create your own formatter following a Swift/SwiftUI convention. Our participant formatter might be re-written like this:

extension Participant {
  enum FormatStyle {
    struct Name: FormatStyle {
      func format(_ participant: Participant) -> String {
        switch participant {
          case .user(let user):
            return User.FormatStyle.Name.format(user)
          case .number(let phoneNumber):
            return PhoneNumber.FormatStyle.Name.format(phoneNumber)
        }
      }
    }
  }
}

As it's more verbose I would be cautious about using it. But that might come in handy in some cases 🙂

Conclusion

ViewData and Formatters are slightly similar: take the former, change the naming and use static functions and you get the later.

However formatters give a complete different perspective about UI problem by being isolated logic and allowing us to compose them whenever needed.

In the end they are an easy and yet very efficient way to share UI formatting across your views. Showing errors for instance become a piece of cake 🍰 using this strategy.

]]>
https://swiftunwrap.com/article/designing-http-framework-requestDesigning a HTTP framework: type safe requestLet's talk about how to build a type safe request for our network frameworkhttps://swiftunwrap.com/article/designing-http-framework-requestTue, 8 Mar 2022 00:00:00 +0000Previously in Designing a lightweight HTTP framework: foundation we added some functionalities on top of Foundation framework.

This time let's go one step further by enhancing URLRequest.

Superpower

Like in previous article let's analyse what we have and our needs. URLRequest has everything to make a request 👍. However what we also want is:

  • The ability to have type safe information. Being for the http method, the body, etc...
  • Having some information about the return type (later called output)

In order to bring all these functionalities this time we'll create a new type. It's name? Request! 🥸

The design I will use is highly inspired from Building type-safe networking in Swift so I encourage you to read it before.

Here's the final type:

struct Request<Output> {
  let path: String
  let method: Method
  let body: Encodable?
  let parameters: [String: String]
  private(set) var headers: [HTTPHeader: String] = [:]
}

As you can see it's pretty similar yet still a little bit different from the article I mentioned before. Let's see what changed.

HTTPHeader

HTTPHeader is a struct encapsulating the header key.

struct HTTPHeader: Hashable, ExpressibleByStringLiteral {
  let key: String
}

Why creating such a simple type? 🧐 Big advantage is the ability to define constant headers. Not only will they be available in autocompletion but it avoid me mistyping the header 😅🎉.

extension HTTPHeader {
    static let accept: Self = "Accept"
    static let authentication: Self = "Authentication"
    static let contentType: Self = "Content-Type"
}

Body

I also needed to add a body property for obvious reasons. I decided to make it Encodable instead of Data. This avoid passing the decoder manually every time we want to set the property.

Plus it make the property more aligned with our need: we send encodable objects like a user, a review, etc... not an obscure data. So it make apis more expressive.

This came with an interesting challenge though: How to set my body type?

First version was declared as is:

struct Request<Body: Encodable, Output> {
    let body: Body?
}

This is because we need a concrete type when using a decoder. Not doing so would lead to an error message "Protocol 'Encodable' as a type cannot conform to the protocol itself".

But keeping (and defining) the body type felt useless: we don't care about it. Plus Request<UserBody, UserPayload> is harder to read than Request<UserPayload>.

So I decided to use a technique known as "opening an existential": accessing the concrete type from the protocol itself. This open the door of bypassing the generic limitation I faced by adding a method where the protocol encode itself:

extension Encodable {
    func encoded<Encoder: TopLevelEncoder>(with encoder: Encoder) throws -> Data {
      // here self conform to Encodable so we can use it with generics
      try encoder.encode(self)
    }
}

Now instead of doing JSONEncoder().encode(someObject) we can actually do someEncodable.encoded(with: JSONEncoder()) 🧙‍♂️.

This in turn simplified URLRequest.encodedBody that we defined in the first article:

mutating func encodeBody<Encoder: TopLevelEncoder>(_ body: Encodable, encoder: Encoder) throws {
  httpBody = try body.encoded(with: encoder)
  // we also use our new `HTTPHeader` type ;)
  setHeaders([.contentType: type(of: encoder).contentType.value])
}

And that, kids, is how I made Request.body being of type Encodable 🥰.

Conclusion

We now have a strongly typed object storing request information. In next article we'll see how to use it alongside URLSession to perform our requests.

]]>
https://swiftunwrap.com/article/swiftui-bugsSwiftUI bugs and defectsLet's dive into some bugs you might encounter while developing with SwiftUI and how to resolve them.https://swiftunwrap.com/article/swiftui-bugsTue, 1 Feb 2022 00:00:00 +0000When working on a SwiftUI app you probably experienced double-edge sword effect: while tremendously pleasant to use SwiftUI especially with teammates you also have to face unexpected/uncomprehensible/weird bugs or behaviours.

That's exactly what happened to me. Here is a list of some of the most unexpected bugs I faced while developing in SwiftUI.

This article report bugs present in iOS 14+.

StateObject not deallocated

When supporting iOS 14+ you start using a lot of @StateObject here and there. It's a very useful property wrapper because the object lifecycle is linked to the view.

Nonetheless you start seeing strange behaviours (including crashes) in your app. After investigating you discover that your @StateObject is actually... not deallocated when the view is destroyed 😱

Maybe you did something wrong? Well maybe not! If you use a NavigationView you actually need to add an extra line in order for you @StateObject to be released upon view destruction: .navigationViewStyle(StackNavigationViewStyle()).

Your StateObject will now get deallocated at the same time than your views. Does it make sense not having this behaviour by default? No. Welcome in SwiftUI (weirdnesses).

StateObject reallocated when going background/foreground

This is probably one of the strangest bug I've ever seen in SwiftUI: everytime you go background/foreground your @StateObject is recreated making you lose data.

If you still use .navigationBarItems (deprecated in iOS 14) then look no further: replacing it with .toolbar fix the issue!

/// before: StateObject reallocated every time the scenePhase become active
.navigationBarItems(trailing: saveButton)

/// after: StateObject is allocated once
.toolbar { saveButton }

View not dismissed

You just fixed previous bug just to find out you get another one... When trying to dismiss a view it does not work anymore! 😤

/// the view is presented but does not dismiss using `dismissButton`
.toolbar {
  createUserButton
    .sheet($createUser) {
      CreateUserScreen()
        .toolbar { dismissButton }
    }
}

Well seem sheet is not playing well inside toolbar. You need to put it outside in order for it to work again:

/// the view is presented but does not dismiss using `dismissButton`
.toolbar {
  createUserButton
}
.sheet($createUser) {
  CreateUserScreen()
    .toolbar { dismissButton }
}

Property wrapper didSet called multiple times

If you use didSet on property wrappers beware that on iOS 15 didSet is called twice with a TextField binding. You'll have to add extra check to avoid it.

As a general note be careful when using didSet on a property wrapper: on regular attributes didSet is not reentrant but on property wrapper it is!

var currentLevel: Int = 0 {
  didSet {
      if currentLevel > AudioChannel.thresholdLevel {
        // this won't cause currentLevel.didSet to be called again
        currentLevel = AudioChannel.thresholdLevel
      }
  }
}

@Published var currentLevel: Int = 0 {
  didSet {
      if currentLevel > AudioChannel.thresholdLevel {
        // currentLevel is a PropertyWrapper: currentLevel.didSet will be called again
        currentLevel = AudioChannel.thresholdLevel
      }
  }
}

I consider it as a bug and hope seeing it fixed in a upper release. If you think the same then please open a bug report to apple.

]]>
https://swiftunwrap.com/article/state-of-the-art-2021State of the Art 20212021 is coming to an end. A perfect time for me to share with you the tech choices I've made over the year.https://swiftunwrap.com/article/state-of-the-art-2021Tue, 28 Dec 2021 00:00:00 +00002021 is almost over. It's a perfect time to have quick look on the technologies I've been using along the year and where the project I'm working on stand.

Tech stack

I have the huge opportunity to work on a SwiftUI project. So my tech stack is quite modern:

  • 🎨 SwiftUI
  • 🔀 Combine
  • 🛠 Xcode 12.x
  • 📦 SPM

SwiftUI

Most of the interface is developed using SwiftUI. It's a delight using this technology.

However while coding quicker and having less conflicts in our pull requests we faced some unexpected UI bugs due to the framework maturity. That's probably the biggest concern about SwiftUI for now: too many bugs, not as trustworthy as UIKit.

What about UIKit then? We use it a little bit when facing limitation with SwiftUI. Luckily for us we dropped iOS13 support in the middle of the year. We're now in process of migrating those few components in plain SwiftUI thanks to the latest features iOS14 give us access to (@StateObject, ScrollViewReader, ...).

Combine

Using SwiftUI we are de-facto using Combine for handling asynchronous calls. It work well although like with any other reactive library my main concern is the same: use it widely. I still see some of my teammates doing too complex, non readable reactive code. With great power comes great responsibility.

async/await should help easing some of our asynchronous code. Sadly we did not migrate yet to Xcode 13 so it will be for next year.

Xcode

As previously told we did not migrate to Xcode 13. We faced bugs while trying to compile our project with it and due to reported bugs decided to wait before using it. Xcode 13.2.1 might be the right one 🤷‍♂️

SPM

We exclusively use Swift Package Manager and dropped our last Carthage dependencies. It work pretty well despite a bug preventing compilation every time we switch branch (hopefully fixed in Xcode 13 🤞).

We're also very excited about upcoming features in next swift version!

Architecture

The application architecture did not change a lot throughout the year and we are mainly using:

  • 🧱 MVVM
  • 💾 CoreData

MVVM

Code is written following MVVM architecture. It work well with SwiftUI and we do not face any specific issue albeit not having injection. That's one of our main goal for 2022.

We also started migrating our views to Smart/Dumb component approach. We saw great improvements in our codebase:

  • Less stateful views and less views owning a ViewModel
  • Less views depending from a ViewModel

This turned into having less bugs 🐛💥

CoreData

Application data are stored locally using CoreData. And like 90% of the projects where I saw CoreData usage... that's pretty much useless 😉

So we're working on two things:

  1. Introducing a domain model to be less dependent from CoreData
  2. Removing CoreData usage when possible

All this stuff is not finished yet and more complex than I though because of lack of knowledge on domain model from some of my teammates. Next time I'll be more vigilant to not only explain what we're going to do but also how.

But we already see improvements:

  • We have less optionals so less edge cases
  • We have more expressive models so the code is easier to understand for newcomers
  • On global it better reflect the app functionalities/vocabulary

Environment

The environment is where most changes happened along the year. Our final stack is:

  • 🦊 Gitlab
  • 🚀 fastlane
  • 🤖 Bitrise

Gitlab

We used to use Bitbucket and was very happy to move to Gitlab over the year. We gained a better UI, labels for MR and a few other things.

In the meantime we lost some functionalities we did not expect:

  • No todo list on the MR
  • Squashing MR produce 2 commits: one squash commit AND one merge commit 😡

Fastlane

While moving from Bitbucket to Gitlab we decided to start using dotenv files to extract environment values and ease transition from one CI to another. Still some improvements we could make on 2022.

Bitrise

We changed our CI to Bitrise. Having used it before on many projects I was very happy to use it again. The tool is still easy to take in charge and migration was quite easy thanks to the few changes we made to our fastlane files.

That being said I was very disappointed on the new announced pricing. We're paying €400/month for 5 developers! Our build time being around 10-12min (so not very long) it seem over priced in my opinion.

Future direction

2021 was the year where we started a lot of subjects to stabilize our code base. 2022 should be the achievement year and see the app go to a next level:

  • 🛠 Xcode 13 migration
  • 🎭 Deprecation of our last UIKit components
  • 🏁 Domain model everywhere and CoreData drop
  • 💉 Dependency injection
  • 🚦 async/await
]]>
https://swiftunwrap.com/article/designing-http-framework-foundationDesigning a lightweight HTTP framework: foundationBring URLSession to a whole next levelhttps://swiftunwrap.com/article/designing-http-framework-foundationTue, 30 Nov 2021 00:00:00 +0000"Nowadays no need for a framework like Alamofire just use URLSession". This is the sort of affirmation that I hear quite often at work.

While maybe a little bit presumptuous it does leave an opened question: what is missing to URLSession that still require to use a framework? How much would we need to add on top of it to have a Alamofire equivalent library?

Let's try asking this question by going into a journey where we'll build a lightweight 🪶 HTTP library to create and handle our requests to see how much effort it will require us.

From idea to the final result including all the questions/mistakes we can make along the way let's see step by step how to do it. First stop: paving the foundation!

You can find the library on Github

Shaping our design

To start our framework we first need to think WHY we need one. It might come for three reasons:

  1. Missing functionalities
  2. Functionalities are there but API could be improved
  3. Code repetition

Which one is it? To help us there's nothing better than using a sample code:

var request = URLRequest(url: URL(string: "https://myapi.com/users")!)
let decoder = JSONDecoder()
let encoder = JSONEncoder()

request.httpMethod = "POST"
request.body = try! encoder.encode(CreateUserBody(username: "swiftunwrap"))
request.setValue("application/json", forHTTPHeaderField: "Content-Type")

URLSession
  .shared
  .dataTaskPublisher(for: request)
  .decode(type: CreateUserResponse.self, decoder: decoder)
  .eraseToAnyPublisher()

This code make a POST request by sending a CreateUserBody then parsing the result as a CreateUserResponse. Let's analyse it and find our "why".

Why: Improvements

Our example is short but missing cases that could make it longer:

  • ⛔ Error handling (from encoding and decoding)
  • ✅ Response validation
  • 👤 Authenticating a request
  • ♾️ Retrying a auth failed request

Thinking broader than just authentication we can rewrite these points as:

  • ⛔ Error handling (from encoding and decoding)
  • ✅ Response validation
  • 👤 Modifying a request before sending it (mostly for authentication)
  • ♾️ Retrying failing requests (mostly for authentication)

Code is also too long for reuse: there would be lots of duplicated code every time we write a request. Ideally we'd like to reduce it to the bare minimum: sending a request (and receiving the response).

session.dataTaskPublisher(for: URLRequest.post("users", CreateUserBody(username: "swiftunwrap")))

Now we know our framework will actually respond to two "why":

  1. Filling missing functionalities
  2. Providing a shorter (and safer) API

Basics: Our needs

But focusing on the "why" is actually only half of the job. We also need to fill the "basics": everything we do with vanilla code. Looking again to our example we need to:

  • Create a URLRequest
  • Send an encoded body
  • Decode a response
  • Having a Combine compatible API (we may also consider async/await support now that it's been made backward compatible)

Less visible but it should also be very important to have small testable methods.

The "why" and needs are our ultimate objectives. We can use them as a to-do list to build our library step by step 👣.

We can even start right away by improving Foundation API by:

  • Validating a HTTP response
  • Simplifying HTTP body creation

Foundation++

Validation

Let's add a URLResponse.validate method:

extension HTTPURLResponse {
  func validate() throws {
    guard (200..<300).contains(statusCode) else {
      /// You can find HttpError implementation here: https://github.com/pjechris/Http/blob/main/Sources/Http/HttpError.swift
      throw HttpError(statusCode: statusCode)
    }
  }
}

extension URLSession.DataTaskPublisher {
  func validate() -> some Publisher {
    tryMap {
      try ($0.response as? HTTPURLResponse)?.validate()
      return $0
    }
  }
}

With just this method we can rewrite our example and validate our HTTP response 👌.

URLSession
  .shared
  .dataTaskPublisher(for: request)
  .validate()
  .decode(type: CreateUserResponse.self, decoder: decoder)
  .eraseToAnyPublisher()

Encoding

Instead of manually encoding and setting a content type let's add an API that do that for us:

extension URLRequest {
    func encodedBody<Body: Encodable>(_ body: Body, encoder: JSONEncoder) -> Self {
      var request = self
      return request.encodeBody(body, encoder: encoder)
    }

    mutating func encodeBody<Body: Encodable>(_ body: Body, encoder: JSONEncoder) {
      httpBody = try encoder.encode(body)
      setValue("application/json", forHTTPHeaderField: "Content-Type")
    }
}

And let's rewrite our example:

let decoder = JSONDecoder()
let encoder = JSONEncoder()
var request = try! URLRequest(url: URL(string: "https://myapi.com/users")!)
  .encodedBody(CreateUserBody(username: "swiftunwrap"), encoder: encoder)

request.httpMethod = "POST"

URLSession
  .shared
  .dataTaskPublisher(for: request)
  .validate()
  .decode(type: CreateUserResponse.self, decoder: decoder)
  .eraseToAnyPublisher()

It's now easier than ever 🍋.

We could also consider adding httpMethod in encodedBody. Both have different meaning and based on what we'll do next it wouldn't make sense.

Our implementation has some caveats thought: it only support JSONEncoder. It might be OK as most API use JSON. But supporting any encoder would be nice.

To do that let's add a ContentType protocol linking an encoder with its HTTP content type:

protocol ContentType {
  /// the http content  type
  var contentType: String { get }
}

extension JSONEncoder: ContentType {
  var contentType: String"application/json" }
}

mutating func encodeBody<Body: Encodable, Encoder: TopLevelEncoder>(_ body: Body, encoder: Encoder) {
  httpBody = try encoder.encode(body)
  setValue(encoder.contentType, forHTTPHeaderField: "Content-Type")
}

Conclusion

By bringing new API to Foundation we only started to scratch our network framework. These API are small improvements but they will shape how we'll code: by bringing small increments/changes to form a lightweight but efficient HTTP library.

In next article we'll make a counterpart to URLRequest.

]]>
https://swiftunwrap.com/article/combine-tips-tricksMastering Combine: tips & tricksHow you can make your Publishers functional and simpler than ever by just following a few tips.https://swiftunwrap.com/article/combine-tips-tricksTue, 28 Sep 2021 00:00:00 +0000Combine is a reactive programming framework allowing you to process asynchronous values over time. What would have been written in 30 lines with delegates can now be written in just few ones.

But while fantastic Combine, as any other reactive framework, come with one downside: complexity. With so many operators it's not always easy to use the right one or know its characteristic. As such what seemed simple to implement might end up with many mistakes.

Let's take this publisher fetching contacts from server everytime our query change:

$queryString
  .removeDuplicates()
  .flatMap { [weak self] in
    self?.contactGateway
      .searchContacts(query: $0)
      .replaceError(with: [])
      .eraseToAnyPublisher()
    ?? CurrentValueSubject([]).eraseToAnyPublisher()
  }
  .assign(to: \.searchResult, on: self)
  .store(in: &cancellables)

When $queryString is modified we call searchContacts and assign the result to searchResult. At first glance the code seem valid and doing the job. Easy peasy 🍋. Is it?

Retain cycle

First thing to be aware of and careful about when using Combine is retain cycles.

When calling sink or assign:

  1. We create a subscriber subscribing to our publisher
  2. Our subscriber keep a strong reference on the publisher
  3. We receive a Cancellable token which retain the subscriber.

This token is then often stored in self.cancellables ending with self retaining the token, the subscriber and the publisher. Thus we need to be careful to not use self in our operators closures to avoid a retaining cycle between self and those items. Hence the use of [weak self].

So far so good. But if we have a closure closer look we can see we use self when calling assign. Not being a closure we might think it's okay. But reading the doc it clearly state the "operator maintains a strong reference to object (self)". Welcome you, hidden retain cycle! 👋

To avoid this pitfall we have two possibilities:

  1. Create a custom operator keeping a weak reference on the object.
  2. Use the alternate assign(to:) storing the token on the publisher itself and available in iOS14.

This article not being so much about this (well known) issue we'll just go with the later one:

$queryString
  .removeDuplicates()
  .flatMap { [weak self] in
    self?.contactGateway
      .searchContacts(query: $0)
      .replaceError(with: [])
      .eraseToAnyPublisher()
    ?? CurrentValueSubject([]).eraseToAnyPublisher()
  }
  .assign(to: &$searchResult)

No more retain cycle although we instead gained an awkward &$ symbol 🤷‍♂️. But we actually still have a bug.

FlatMap vs SwitchToLatest

It's probably one of the most common mistake made in Reactive programming whether being in RxSwift or Combine: the misusage of flatMap operator.

Remember our objective? Every time our query ($queryString) change we want to search our contacts. Is it our signal behaviour? Not exactly.

Every time user enter a character we create a new signal to search for our contacts. But we don't cancel previous ones. Therefore all will execute and respond but in undefined order. We might then get the result for "bruc" after "bruce" and not display the correct UI.

One solution would be to use debounce:

$queryString
  .removeDuplicates()
  .debounce(0.5, RunLoop.main)
  .flatMap { [weak self] in
    self?.contactGateway
      .searchContacts(query: $0)
      .replaceError(with: [])
      .eraseToAnyPublisher()
    ?? CurrentValueSubject([]).eraseToAnyPublisher()
  }
  .assign(to: &$searchResult)

By reducing the number of call to searchContacts we mitigate the bug but if we have network latency it might rise again. What we truly need is to keep only latest search request running and cancel any previous one.

Thankfully Combine does provide an operator to achieve just that: switchToLatest. As its name imply switchToLatest switch on the latest signal made and cancel previous ones.

$queryString
  .removeDuplicates()
  .debounce(0.5, RunLoop.main)      // we can keep our debounce
  .map { [weak self] in
    self?.contactGateway
      .searchContacts(query: $0)
      .replaceError(with: [])
      .eraseToAnyPublisher()
    ?? CurrentValueSubject([]).eraseToAnyPublisher()
  }
  .switchToLatest()
  .assign(to: &$searchResult)

We had to replace flatMap with map in order to use switchToLatest

Now when our query change from "bruc" to "bruce" previous request will be canceled and only the one about "bruce" will run and return its result.

CurrentValueSubject vs Just

Inside our closure we use CurrentValueSubject when self is weak. While working I think it is semantically wrong.

searchContacts is a publisher that (probably) send only one value and complete. On the other hand CurrentValueSubject send one value and... never complete. We can also consider when self is weak that we won't do anything else so more reason to complete our inner signal.

Also while writing this article I discovered that as long as a inner publisher is not completed it stay alive no matter if the upstream was completed or canceled. In this regard I would suggest to not use CurrentValueSubject to send only one value.

What would be a more appropriate answer then? Using Just as it just send one value then automatically complete.

$queryString
  .removeDuplicates()
  .debounce(0.5, RunLoop.main)
  .map { [weak self] in
    self?.contactGateway
      .searchContacts(query: $0)
      .replaceError(with: [])
      .eraseToAnyPublisher()
    ?? Just([]).setFailureType(to: Error.self).eraseToAnyPublisher()
  }
  .switchToLatest()
  .assign(to: &$searchResult)

We had to change Just error type in order to make it compatible with our searchContacts error type

Weak self

Our closure is quite verbose because of the use of weak self to avoid retain cycles. But if we look to our code we never really use self itself. What we are interested in is contactGateway.

In Swift we can define a closure capture list using [] syntax. By explicitly capturing contactGateway we get a strong reference to it and avoid referencing self. We can then drop our Just publisher.

$queryString
  .removeDuplicates()
  .debounce(0.5, RunLoop.main)
  .map { [contactGateway] in
    contactGateway
      .searchContacts(query: $0)
      .replaceError(with: [])
  }
  .switchToLatest()
  .assign(to: &$searchResult)

Because we now have only one signal inside map we could even get rid of eraseToAnyPublisher!

Conclusion

Following those small rules should hopefully help you simplify quite a bit your streams! So remember:

  1. Be careful about retain cycles
  2. Prefer switchToLatest over FlatMap
  3. Prefer Just over CurrentValueSubject
  4. Avoid referencing self and capture your attributes instead
]]>
https://swiftunwrap.com/article/cocoapods-script-phasesCocoapods script phasesToday let's dive in Cocoapods and how we can gracefully handle our build scripts thanks to script phases feature.https://swiftunwrap.com/article/cocoapods-script-phasesTue, 10 Aug 2021 00:00:00 +0000A few years ago Cocoapods landed a long waited functionality: script phases.

Back at the time the blog did not exist so with a little delay let's have a look on how using them through two examples: SwiftLint and SwiftGen.

This is an updated version of an article I wrote some time ago in French 🇫🇷

SwiftLint

Let's start by defining a script called "🚨 SwiftLint" right into our Podfile. We will make it execute before code compilation.

target :myApp do
  pod SwiftLint
  pod ...

  script_phase {
    :name => '🚨 SwiftLint',
    :script => '"${PODS_ROOT}/SwiftLint/swiftlint"',
    :execution_position => :before_compile
  }
end

Once we run pod install Cocoapods add the script phase into our xcodeproj build phases. SwiftLint will then be executed by Xcode (or xcodebuild) when compiling.

And... that's all 🤷‍♂️. As we did not define any input nor output files the script phase will get executed every time we compile.

Make sure to surround environment variable with double quotes (")

SwiftGen

target :myApp do
  pod SwiftGen
  pod ...

  script_phase {
    :name => '🛠️ SwiftGen (Generate resources)',
    :script => '"${PODS_ROOT}/SwiftGen/bin/swiftgen" config run --config .swiftgen.yml && touch .swiftgen.yml"',
    :execution_position => :before_compile,
    :input_files => ['.swiftgen.yml'],
    :output_files => ['Generated/Assets.generated.swift', 'Generated/Fonts.generated.swift']
  }
end  

Here we added two new parameters:

  • input_files to define which files, when modified, trigger the script to run while building
  • output_files to define files the script generate

You can also use {input/output}_file_lists to define input/output files

You might wonder why adding .swiftgen.yml as input file and touching it? Script execution is based on both input and output files. If output files exist then script won't run again unless input files changed. So to make our script run every time we have to hack a little bit.

Going deeper

Up until know we defined our script phases in Podfile. While convenient at first it can quickly increase our file length.

To mitigate this issue and make our code more readable let's move our scripts into a dedicated file: Phasesfile (name is arbitrary).

module ScriptPhase
  def self.swiftlint
    {
      :name => '🚨 SwiftLint',
      :script => '"${PODS_ROOT}/SwiftLint/swiftlint"',
      :execution_position => :before_compile
    }
  end

  def self.swiftgen
    {
      :name => '🛠️ SwiftGen (Generate resources)',
      :script => Script.swiftgen,
      :execution_position => :before_compile,
      :input_files => ['.swiftgen.yml'],
      :output_file_lists => ['.swiftgen.outputs.xcfilelist']
    }
  end
end

module Script
  def self.swiftgen
    <<~EOS
      "${PODS_ROOT}/SwiftGen/bin/swiftgen" config run --config .swiftgen.yml
      touch .swiftgen.yml
    EOS
  end
end 

We can now refactor our Podfile:

load 'Phasesfile'

target :myApp do
  script_phase(Phase.swiftlint)
  script_phase(Phase.swiftgen)

  pod SwiftLint
  pod SwiftGen
  pod ...
end

Not only did we improve its readability but we can now also share our Phasesfile with other projects if we want to.

Conclusion

Cocoapods script phases are nothing but simple and handy by giving you the ability to include your custom build steps in your Podfile.

This feature is not yet available in Swift Package Manager but should come as part of SE-303 Extensible Build Tools.

While probably providing more functionality than Cocoapods script phases counterpart it will also be harder to read.

]]>
https://swiftunwrap.com/article/swift-argument-parserScripting with Swift Argument ParserLet's see how we can use Swift Argument Parser to write powerful scripts for our applications.https://swiftunwrap.com/article/swift-argument-parserTue, 27 Jul 2021 00:00:00 +0000Need automating tasks? Write a script! But which language should you use? Until now people used to either use bash or Ruby (at least in iOS environment). But now come a third contender: Swift Argument Parser, an Apple open-source parsing command line library. Let's see what we can do with it.

Swift Argument Parser

Let's say we have an application where we have to import localizations from a CSV file. We don't want to do it manually though so let's use Swift Argument Parser to automate the task.

Let's first open our Package.swift and add it as dependency.

Don't already have a Package.swift file? Just run swift package init --type executable to initiate a new library.

In the meantime, let's add a CSV parsing library and define a Import target which will be responsible to import localizations.

import PackageDescription

let package = Package(
    name: "Scripts", // the name here don't really matter
    products: [
        .executable(name: "import", targets: ["Import"])
    ],
    dependencies: [
        .package(url: "https://github.com/apple/swift-argument-parser", .upToNextMajor(from: "0.3.2")),
        .package(url: "https://github.com/yaslab/CSV.swift", .upToNextMajor(from: "2.4.3"))
    ],
    targets: [
        .target(name: "Import",
            dependencies: [
                .product(name: "ArgumentParser", package: "swift-argument-parser"),
                .product(name: "CSV", package: "CSV.swift")
            ]
        )
    ]
)

We can now start implementing our command in Sources/Import/main.swift file.

import ArgumentParser

struct Import: ParsableCommand {
    func run() throws {
        let filePath = "myFile.csv"
        let dstPath = "MyApp/Resources"
        let csvReader = try CSVReader(stream: InputStream(fileAtPath: filepath)!, hasHeaderRow: true)

        let content = generateLocalization(from: csvReader)

        FileManager.default.createFile(
            atPath: dstPath.appendingComponentPath("Localization.strings"),
            contents: localization,
            attributes: [:]
        )
    }

    func generateLocalizations(from reader: CSVReader) -> String {
        // we read CSV and transform it into a .string compatible format
    }
}

Import.main()

Xcode has native support for Swift packages. Just open Package.swift with it and voila! 💪

That's great but all our values are hardcoded. Let's add some parameters to make it more powerful.

Property wrappers

Swift Argument Parser provide three property wrappers to parse command line parameters:

  • Argument which is a plain value from the command line, like myFile.csv
  • Option, a "key=value" parameter. For instance --source=myFile.csv
  • and finally Flag which is key only, i.e --verbose

All these property wrappers are deeply explained in Swift Argument Parser documentation.

Let's apply these property wrappers to our code to define two parameters: the CSV source file path and the location where we want to store generated content.

struct Import: ParsableCommand {
    @Argument(help: "csv file path to load")
    var source = "localizations.csv"

    @Option(help: "filepath where to store generated .strings files")
    var destinationPath = "MyProject/Resources/Localization.strings"

    func run() throws {
        let csvReader = try CSVReader(stream: InputStream(fileAtPath: source)!, hasHeaderRow: true)

        let content = generateLocalizations(from: csvReader)

        FileManager.default.createFile(
            atPath: destinationPath,
            contents: content,
            attributes: [:]
        )
    }
}

With only these few lines we can now invoke our command to import localizations:

$ swift run import
$ swift run import someFile.csv
$ swift run import someFile.csv --destinationPath=/Users/swiftunwrap/i18n.strings
$ swift run import --destinationPath=/Users/swiftunwrap/i18n.strings

Interacting with the app

What's really great about Swift Argument Parser is that we can actually interact with our app code.

Let's say we have a SupportedLocale type in our app. Each time we add a new locale to the app we also want to make sure that we import its translations.

// file: MyProject/Domain/SupportedLocale.swift

enum SupportedLocale: String, CaseIterable {
    case english
    case french
    case portugese
}

To make that check let's add a new target in our Package.swift:

let package = Package(
    name: "Scripts",
    products: [
        .executable(name: "import", targets: ["Import"])
    ],
    dependencies: [
        .package(url: "https://github.com/apple/swift-argument-parser", .upToNextMajor(from: "0.3.2")),
        .package(url: "https://github.com/yaslab/CSV.swift", .upToNextMajor(from: "2.4.3"))
    ],
    targets: [
        .target(name: "Domain", dependencies: [], path: "MyProject/Domain"),
        .target(name: "Import",
                dependencies: [
                    "Domain",
                    .product(name: "ArgumentParser", package: "swift-argument-parser"),
                    .product(name: "CSV", package: "CSV.swift")
                ]
        )
    ]
)

By adding Domain target and declaring it as Import dependency we now have access to our app models right from Import. We can now check that each CSV translation is available in all languages:

import ArgumentParser
import Domain

struct Import: ParsableCommand {
    @Argument(help: "csv file path to load")
    var source = "localizations.csv"

    @Option(help: "filepath where to store generated .strings files")
    var destinationPath = "MyProject/Resources/Localization.strings"

    func run() throws {
        let csvReader = try CSVReader(stream: InputStream(fileAtPath: source)!, hasHeaderRow: true)

        let content = try generateLocalizations(from: csvReader)

        FileManager.default.createFile(
            atPath: destinationPath,
            contents: localization,
            attributes: [:]
        )
    }

    func generateLocalizations(from reader: CSVReader) -> String throws {
        while csvReader.next() != nil {
                guard let row = csvReader.currentRow, row.first(where: { !$0.isEmpty }) != nil else {
                    continue
                }

                // to simplify example we'll just use rawValue
                if let missingLocale = SupportedLocale.allCases.first(where: { row[$0.rawValue] == nil }) {
                    throw LocalizationError.missingLocale(missingLocale)
                }
                
                // treat the translation   
            }
        }
    }
}

It might not be the best example in the world but I hope you get the point: you can reuse code from your application right from your scripts!

Conclusion

Swit Argument Parser is a very handy tool when needing to write scripts for your projects.

Not only does it provide a safer environment than Bash but it also bring ability to share code between your scripts and your applications. It can open the door to whole new interesting usage!

]]>
https://swiftunwrap.com/article/smart-dumb-viewsSmart and Dumb views in SwiftUILet's see how organizing our views into stateful and stateless views can greatly simplify our codebase.https://swiftunwrap.com/article/smart-dumb-viewsTue, 6 Jul 2021 00:00:00 +0000Making a SwiftUI view is nothing but simple. It just takes a few lines of code.

Let's take this view for instance:

struct MessageScreen: View {
    @StateObject var viewModel = MessageViewModel()

    var body: some View {
        List {
            ForEach(viewModel.messages) { message in
                HStack {
                    Button(action: { viewModel.edit(message) }) {
                        VStack {
                            Text(message.sentAt.formatted())
                                .font(.footnote)
                            Text(message.content)
                        }
                    }

                    Spacer()

                    Button(action: { viewModel.delete(message) }) {
                        Image(systemName: "minus.circle.fill")
                    }
                }
            }
        }
        .onAppear(perform: viewModel.loadMessages)
        .navigationTitle("Mesages")
    }
}

At first glance there is not much to to say about. It is quite simple and functional. A very classic SwiftUI view.

Still, we can notice we are doing two things inside it:

  • 🎨 Layout, as we loop over viewModel.messages to display them
  • ⌨️ Data handling through onAppear and @StateObject

Beside this mixing we are also hitting a limit: we can't display a list of messages without first loading them. Neither can we make multiple previews without instantating a ViewModel which might require multiple dependencies.

struct MessageScreen_Previews: PreviewProvider {
    static var previews: some View {
        // How to display view in multiple states?
        // How to avoid to instantiate all (potentialy numerous) ViewModel dependencies?
        MessageScreen()
    }
}

To bypass these issues let's refactor our view into a smart and dumb component.

Smart or dumb?

You probably already heard about this pattern as it is quite popular in React ecosystem. If not Smart and Dumb components (also known as Container and Rendering views or Container and Presentational views) are a way to split your views into two categories depending on their state.

Smart views are stateful: they handle data state and view lifecycle. They don't manage layout and thus delegate this task to their sibling: dumb views.

Dumb views are used to render data only. In effect they receive data through init and thus don't store any internal state. Therefore compared to smart views they are stateless.

In iOS world these concepts can be mapped to Property wrappers.

Smart viewDumb view
@State
@StateObject
@ObservedObject
@Binding
@EnvironmentObject
@Environment
@Namespace

As (most) property wrappers indeed lead to state handling you should use them only in smart views. Exceptions being Environment and Binding.

Bindings are fine because they are equivalent as passing two functions, get: () -> T and set: T -> ().

Environment is fine as it does not incur any state.

Time to compose

Let's try applying this approach to our example by first extracting our layout into a new view:

struct MessageListView: View {
    let messages: [Message]
    let edit: (Message) -> Void
    let delete: (Message) -> Void
    
    var body: some View {
        List {
            ForEach(messages, content: row(message:))
        }
    }
    
    @ViewBuilder
    func row(message: Message) -> some View {
        HStack {
            Button(action: { edit(message) }) {
                VStack {
                    Text(message.sentAt.formatted())
                        .font(.footnote)
                    Text(message.content)
                }
            }
            
            Spacer()
            
            Button(action: { delete(message) }) {
                Image(systemName: "minus.circle.fill")
            }
        }
    }
}

Important thing to note is that this view has no state: messages and actions are passed at initialization. Therefore we can consider this view as being a dumb view.

Passing closures to dumb views to execute an action is a very common scenario.

We then update MessageScreen by invoking our new MessageListView:

struct MessageScreen: View {
    @StateObject var viewModel = MessageViewModel()
    
    var body: some View {
        MessageListView(
            messages: viewModel.messages,
            edit: viewModel.edit,
            delete: viewModel.delete
        )
        .onAppear(perform: viewModel.loadMessages)
        .navigationTitle("Messages")
    }
}

MessageScreen still reference StateObject and onAppear so it is definitely handling some state and can be called a smart view.

We can now have two views, one handling view state and the other one rendering it. At this point you might wonder though: so what? What did we gain?

What's the point?

At first glance it might seem we did not bring much value to our code. We even increased code size and went from one to two views!

However taking a closer look we can see:

  • ♻️ We gained a reusable view (MessageListView)
  • 👓 We can easily preview our dumb view
  • ✂️ Last but not least most of our code is tech agnostic. Whether you decided to go with a ViewModel, Redux or Composable Architecture, your dumb view does not depend on it. Therefore you can switch your architecture without impacting it!
// We can now easily make previews of our view
struct MessageList_Previews: PreviewProvider {
    static var previews: some View {
        MessageListView(
            messages: [
                .init(id: UUID(), content: "Welcome on Swift Unwrap", sentAt: Date())
            ],
            edit: { _ in },
            delete: { _ in }
        )
    }
}

Conclusion

Smart/Dumb components is very simple and is just a way of thinking how to organize your state in your views.

Use it wisely (make few smart views and a lof of dumb views) and you will avoid many data synchronization pitfallss and reduce view coupling to your architecture. So for once, don't try to be smart, be dumb!

]]>
https://swiftunwrap.com/article/deploy-blog-using-github-actions-and-awsBuilding a blog: Deploying using Github Actions and AWSAfter making our blog with Publish it is now time to publish it using Github Actions!https://swiftunwrap.com/article/deploy-blog-using-github-actions-and-awsTue, 20 Apr 2021 00:00:00 +0000In Building a blog with Publish we saw how to make a website using Publish. Today let's see how we can automate its deployment using Github Actions and AWS.

Github Actions

Github Actions is quite new compared to other solutions as it was made available back in... 2020 😯.

To use it all you need to do it is create Yaml files into a .github/workflows root directory. Each one define a workflow that will be automatically executed on your repository based on some events you defined.

When starting the blog I first decided to have one workflow (deployment) handling both preproduction and production environments. While appealing at first because avoiding some code duplication you quickly end up with lots of conditions inside it making very hard to maintain.

So after some try and learn I actually decided to have 2 workflows hence 2 Yaml files:

  • preproduction.yaml
  • production.yaml

Events

First thing to define is events. Events are activity triggering your workflow like pull requests, pushes and so on. I decided to trigger preproduction when opening a pull request or pushing on master while production is triggered on every Tuesday or manually.

# Preprod
on:
  pull_request:
    branches: [master]
  push:
    branches: [master]

# Prod
on:
  # Deployment is scheduled to be trigged on every Tuesday at 14h45 UTC
  schedule:
    - cron: "45 14 * * TUE"
  # This allow me to manually trigger the workflow from Github interface  
  workflow_dispatch: { }

Jobs

Our Yaml next entry is jobs. You can have multiple jobs in a workflow but mines only use one: deploy. It define two things:

  1. A Runner (no link with Cyberpunk 2077) to set the host machine we want to use
  2. The actions we want to run

For now all we need is to checkout the repository, install swift and build our project:

#Preprod
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout repo
      uses: actions/checkout@v2

    - name: Install swift
      uses: YOCKOW/Action-setup-swift@v1
      with:
        swift-version: "5.3.3"

    - name: Generate site preview
      run: swift run SwiftUnwrap --upcoming-articles

#Prod
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout repo
      uses: actions/checkout@v2

    - name: Install swift
      uses: YOCKOW/Action-setup-swift@v1
      with:
        swift-version: "5.3.3"

    - name: Generate site
      run: ENV=PROD swift run      

We don't use a macOS runner because there's no really need for it. Beside Github Actions is limited to 2,000 mins/month on Linux and only 200 mins/month on macOS.

Deployment

Now that our workflow run and build our project we need to deploy it. You get multiple choices to deploy your website: AWS, Scaleway, Heroku,... and Netlify.

I did test Netlify but ended up with AWS for two reasons:

  1. Configuration is very limited and the whole thing felt too much "magic" to me
  2. Previous deployments are not deleted. Meaning deploying 300 times would result in having 300 times my project on servers. It might seem ridiculous to some but to me it is a very negative ecological aspect 🌳🌎.

Netlify being a NoGo I went to use AWS buckets.

AWS

You'll first need to create a AWS S3 bucket on Amazon and configure it for being accessible as a website.

Once done we then need to add a step in our Github Workflow to deploy our built website on AWS. Github Actions already come with AWS CLI installed so we won't need extra step to download those.

# Preprod
- name: Deploy to Preprod
  run: |
    aws s3 sync Output $AWS_S3_URI --delete
    aws s3 cp robot.txt $AWS_S3_URI
  env:
    AWS_S3_URI: ${{ secrets.AWS_S3_URI_PREPROD }}
    AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
    AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

# Prod
- name: Deploy to Prod
    run: aws s3 sync Output $AWS_S3_URI --delete
    env:
      AWS_S3_URI: ${{ secrets.AWS_S3_URI_PROD }}
      AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
      AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}    

If your deployment fail and you have 'botocore.utils.BadIMDSRequestError' error, change your runner to ubuntu-18.04.

You should now have a running blog on the web. Congratulations! 🎉

]]>
https://swiftunwrap.com/article/handling-optional-in-swiftui-viewHandling optional in SwiftUI viewLet's have a look to how handling optional content in SwiftUI without relying on heavy `if let` everywhere in our codebase.https://swiftunwrap.com/article/handling-optional-in-swiftui-viewTue, 30 Mar 2021 00:00:00 +0000Last version of Swift brought many improvements to @ResultBuilder. Yet one thing I still find myself doing often is checking whether my data is nil to pass it to a View like Text. Well today I'd like to share with you a small tip I found about SwiftUI and optionals.

Failable initializer to the rescue!

Some time ago I wanted to refactor some code to make my view code more concise and easier to read:

// move from this...
struct ContentView: View {
  var body: some View {
    if let name = myParticipant.name {
      Text(logicToComputeName(name))
        .font(.bold)
    }
  }
}

// ...to this
struct ContentView: View {
  var body: some View {
    Text(name: myParticipant)
      .font(.bold)
  }
}

So I started to write a custom Text.init:

extension Text {
  init(name participant: Participant) {
    if let name = participant.name {
      self.init(logicToComputeName(name))
    }
    else {
        // what should I do here??
    }
  }
}

But as you can see data might be nil. In that case no Text should be created. How to handle that? Well failable initializer seemed like the way to go. But would it actually work and compile with SwiftUI/ResultBuilder?

extension Text {
  init?(name participant: Participant) {
    guard let name = participant.name else {
      return nil
    }

    Text(participant.name)
  }
}

// Testing with this code
struct ContentView: View {
  var body: some View {
    Text(name: myParticipant)
      .font(.bold)
  }
}

Well... it actually did! But something was strange because I didn't had to unwrap my view in order to use modification functions (.font and so on) 🤔. Only reason I could see is that Optional is probably a View which would explain why we can call modifiers without unwrapping.

To be sure let's try to implementing View on Optional:

extension Optional: View where Wrapped: View { }

You should then see a message from the compiler stating that "Conformance of 'Optional<Wrapped>' to protocol 'View' was already stated in the protocol's module 'SwiftUI'". So Optional is indeed a View.

So we can actually make our SwiftUI View init as failable initializer to optional/empty cases!

For instance we could add a generic failable initializer to Text:

extension Text {
  @_disfavoredOverload
  init?<S: StringProtocol>(_ content: S?) {
    guard let content = content else {
      return nil
    }

    self.init(content)
  }
}

Which make obviously less if let in my view codebase 🎉.

Should we go down that way though? If Apple did not provide Text with such a init there might be (maybe) a reason. While pretty convenient it is true that in the meantime it is less clear the view might not be displayed.

In the end I guess it's mostly a developer choice. On my side I'm using failable initializers only when making custom init and leaved generic ones out of code.

]]>
https://swiftunwrap.com/article/building-blog-swift-publishBuilding a blog: PublishIn these series of article I will tell you about the tools I used in order to make SwiftUnwrap. Let's start by having a look to Publish.https://swiftunwrap.com/article/building-blog-swift-publishTue, 16 Mar 2021 00:00:00 +0000In 2020 I decided to build my own tech blog. While appealing (how hard could it be?) it actually took me some time to figure out which stack to put in: Swift or not Swift; how to deploy a website with modern technologies like AWS; how to automate without costing me anything.

In this first article let's focus on the first choice I made: building my blog using Swift thanks to Publish.

Why Swift?

When building my website I first though about using web technologies like React. But then it came to my mind that:

  1. It's run on client-side meaning I would have needed a backend server to deliver the articles content.
  2. It's written in Javascript/Typescript which I barely know. Sure it would have been the occasion to improve my skills on it. But I also knew that doing so I would actually probably never release my project.

Tools like Gatsby or NextJS would have actually me help on doing a SSG (Static Site Generation) with front technologies. But again I preferred to focus on a language I knew well to reach my goal.

Swift web ecosystem is not as vibrant as the one from Javascript. Available frameworks and tools are way more limited. Fortunately however Swift does have (at least) one known static site generator framework: Publish.

Publish

To use Publish you just need to add it to your Package.swift file. Note that in order to make our blog we will create a executable project, not a library.

let package = Package(
  products: [
    .executable(name: "SwiftUnwrap", targets: ["SwiftUnwrap"])
  ,
  dependencies: [
    .package(url: "https://github.com/johnsundell/publish.git", from: "0.7.0")
  ]

Publish does come with pre-defined code (and folders) structure which you have to follow in order to use it. While the README is quite complete it actually took me some time to really grasp how to use the tool. So here are some notes/advice.

Content

First thing to understand is that your website will be an instance of Website. You can put any data inside it that is related to you website but you'll have at least to provide two associated objects: SectionID and ItemMetadata.

SectionID allow you to classify your content. As I'm only publishing articles mine is actually reduced to only one value:

enum SectionID: String, WebsiteSectionID {
  case article
}

One thing important though: SectionID is used when loading your content. Here for instance I had to put all my articles under an "article" folder in order for them to be associated to the ID (and thus accessible from code).

ItemMetadata give you access to your markdown files metadata. Each attribute will match one of your markdown metadata:

struct ItemMetadata: WebsiteItemMetadata {       
  let path: Path
  let tags: [Tag]
  let socialTags: SocialTags
  let description: String
  let isDraft: Bool
}

WebsiteItemMetadata has no required attributes. Yet some have special treatment from Publish.

There is no documentation on them (at least last time I checked) and you'll have to dig into MarkdownContentFactory.makeItem and MarkdownContentFactory.makeContent to find them. At the time of writing these are:

  • 🛣️ path
  • 🏷️ tags
  • 📅 date
  • 🔗 rss
  • 📰 title
  • 🖊️ ️description
  • 🖼 ️image
  • 🎧 audio
  • 📹 video

They will automatically end up either in Item or Content and will be used by Publish to do certain things like path defining your file HTML URL.

One thing to note is that you can still "redefine" them in ItemMetadata. For instance if you take a closer look to the code above you will see that I defined my own tags attribute using a custom enum Tag which allow me to limit labels I can set on an article.

Bear in mind though that doing so you will end up with the attribute in two places:

ItemMetadata.tags and Item.tags each one having with its own type 🐫.

enum Tag: String, Decodable, CaseIterable {
    case spm
    case ui
    case swift
    case testing
    case ci
    case architecture
    case network
    case build
    case rx
}

Also one note of caution about date: if you don't define any date in your markdown then Publish will automatically pick one (file modification date). So be careful if you're using it to define which article to publish or not 🙂

Theming

A Theme is a set of defined methods throughout which you generate your HTML. There are 6 methods you can define but the most important ones are:

  • makeIndexHTML to make your homepage
  • makeSectionHTML to create a page listing content of the given section
  • makeItemHTML generate HTML page for an item (one markdown file in one section). In my blog that's where I generate an article detail page
  • makeTagDetailsHTML to display content associated to a tag.

You can use these information to display whatever you want using Plot DSL.

func makeIndexHTML(for index: Index, context: PublishingContext<SwiftUnwrap>) throws -> HTML {
    HTML.blog(for: index, context) {
        .div(
            .id("home"),
            .class("content"),
            .section(
                .browseAllTags(context: context)
            ),

            .section(
                .class("latest"),
                .h2("Latest articles"),
                .previewList(
                    context.allItems(sortedBy: \.date, order: .descending)
                )
            )
        )
    }
}

Publish command

Ok now how do you generate your website? You'll have to run Website.publish into your main.swift file.

You can use publish with predefined steps but I like being in control so I defined my own pipeline. Important pieces were:

  • To parse code using Splash
  • Add markdown files
  • Generate HTML
  • Generate RSS and sitemap
  • Copy resources
try SwiftUnwrap().publish(
    using: [
        .installPlugin(.splash(withClassPrefix: "")),
        .group([
            .configureMetadataParsing(),
            // Content is actually under Content/en/article, remember SectionID
            .addMarkdownFiles(at: "Content/en"),
        ]),
        .generateHTML(withTheme: .swiftUnwrap),
        .generateMinimizedCSS(),
        .generateRSSFeed(including: [.article]),
        .generateSiteMap(),
        .copyResources(at: "Resources/images", to: "images")
    ]
)

You'll notice generateMinimizedCSS which is a custom step I made to minimize my website CSS. While not available by default in Publish it is very easy to add by yourself:

extension PublishingStep where Site == SwiftUnwrap {
  static func generateMinimizedCSS() -> Self {
        step(named: "minimize css from Resources folder") { context in
            let minimizedCss = try context.createOutputFile(at: "styles.css")
            let resources = try context.folder(at: "Resources")

            try resources.files
                .filter { $0.extension == "css" }
                .forEach { file in
                    try minimizedCss.append(try file.read())
                    try minimizedCss.append("\n")
            }
        }
    }
}

You can now run swift run and generate your website 👏

To test locally use swift run publish-cli run. Your website will then be accessible on localhost:8000.

Now that we know how to generate our website we can deploy it using Github Actions!

]]>
https://swiftunwrap.com/article/self-validating-modelsSelf validating modelsLet's see how we can avoid building non valid objects that would break our app by using self validation when dealing with complex objects like phone numbers or IBAN.https://swiftunwrap.com/article/self-validating-modelsTue, 5 Jan 2021 00:00:00 +0000Sometimes in your project you might have objects requiring some validation prior being considered as valid such as IBAN or phone number. In the later we often need to ensure user input is a valid phone number. Failing in doing so (or not doing it) could make your app at best behaving strangely and at worst crashing.

We could at first just work with a simple String:

let iban: String = "Not valid"

However by doing so our IBAN might be (and is) invalid. We would also have to check it is actually correct every time we use it. Not doing so could make the app at best behaving strangely and at worst crashing.

To avoid such pitfalls we can use self validating models.

Building a self validating model

First thing to do is to create an IBAN model. At first we'll consider everything data is valid:

struct Iban {
  let value: String

  init(_ string: String) throws {
    self.value = string
  }
}

Using throws we indicate that init might fail if input is invalid 💪. It now require us to be careful when trying to get a Iban instance:

do {
  let iban = try Iban("Not a valid IBAN")
}
catch {

}

We can't now work or ask an invalid Iban in our app without knowing about it 🎉. Well unless you have bugs in your algorithm that is 🤷‍♂️

Testing

One great thing about self validating objects is how your tests become more explicit. Let's write some to see:

class IbanTests: XCTestCase {

  func test__init__withValidIBANFormat__ItReturnAnIban() {
    let string = "FR1420041010050500013M02606"

    XCTAssertNoThrows(try Iban(string))
  }

  func test__init__lengthIsTooShort__IThrow() {
    let string = "FR142"

    XCTAssertThrows(try Iban(string))
  }
}

Right now our second test will fail so let's just implement our algorithm:

struct Iban {
  let value: String

  init(_ string: String) throws {
    self.value = try Self.validating(string)
  }
}

extension Iban {
  private static func validating(_ string: String) throws -> String {
    // take a look at https://en.wikipedia.org/wiki/International_Bank_Account_Number
    // if you ever wanted to implement a real IBAN algorithm
    let ibanLength = 23

    guard string.length == ibanLength else {
      throw IbanParseError.invalidLength
    }

    return string
  }
}

enum IbanParseError: Error {
    case empty
    case invalidLength
    case invalidAlphaNumeric
    case unsupportedCountry
    case invalidChecksum
}

Run your tests again and everything should work! One neat thing about self validating objects is Error. See our IbanParseError? By using it we make it pretty clear why our object might have failed in building. We can also make our tests even more explicit by checking for specific errors:

func test__init__invalidIbanLength__IThrow() {
  let string = "FR142"

  XCTAssertThrowsSpecific(try Iban(string), IbanParseError.invalidLength)
}

Function validation

You might wonder "Why not testing Iban.validating itself?". The answer is simple: because it is not part of our public API. We could make it public/internal but there is no reason to. That's the whole idea behind self validating object: you're asking for an object and just checking you can get it or not. The validation is... implementation details 🤷‍♂️

Conclusion

Self validating objects are nothing but simple. It is all about using objects to represent your app data and integrating the validation inside it instead of having it appart.

It might seem like a small change at first but if you try it you'll see that not only does it make your codebase safer but it also improve its readability.

]]>
https://swiftunwrap.com/article/swiftui-listCustomising a List in SwiftUIIf you started to play along with SwiftUI, chances are you used List quite a bit in your code. And you probably had some unexpected challenges with it. So today let's see how to fix them in iOS13 and... iOS14!https://swiftunwrap.com/article/swiftui-listTue, 1 Dec 2020 00:00:00 +0000Building a List in SwiftUI is a real pleasure compared to its counterpart from UIKit. We went from having a UITableView, a UITableViewDataSource/UITableViewDelegate and a custom cell to just making a simple loop on our data to generate our List 🥳.

But if you ever tried to hide a separator or the NavigationLink arrow from your list then you know that the initial dream 😇 can quickly become a nightmare 😡.

Today let's see how to customise those two items into a List both for iOS13 and iOS14.

iOS13

Table separator

Let's start with iOS13 which you may still need to be compatible with. Hiding the separator is actually pretty easy: you can rely on UITableView appearance!

UITableView.appearance().separatorStyle = .none

Downside of this technique is it will be disabled for the all application. If you need more granular control, one solution might be to rely on SwiftUI-Introspect:

List {
  ...
}
.introspectTableView { tableView in
  tableView.separatorStyle = .none
}

NavigationLink arrow indicator

Disabling the arrow is a little bit "trickier". Basically you'll have to use a ZStack to layer an empty NavigationLink (yes). On top of which you'll actually display your content:

List {
  ForEach(data) {
    ZStack {
      NavigationLink(destination: MyDestination()) {
        EmptyView()
      }
      .opacity(0)

      MyRowContent($0)
    }
  }
}

It's more verbose and less "aesthetic" than regular NavigationLink but it efficiently hide the arrow.

Voila! You customised your app for iOS13. But unfortunately for you... those don't work on iOS14 😬

iOS14

Apple changed some implementation details and so we are back to finding solutions for iOS14. We could use same workaround just as we did on iOS13. However this is far from ideal and we might face new issues when iOS15 is out.

Fortunately for us Apple also introduced LazyVStack this year. So instead of using a List we can instead migrate to a LazyVStack:

ScrollView {
  LazyVStack {
    ForEach(data) {
      NavigationLink(destination: MyDestination()) {
        MyRowContent($0)
      }
    }
  }
}

With this code you will have neither separator nor navigation accessory arrow issues. But what if you still need to support iOS13? You can build a custom view to use use List or LazyVStack.

struct CompatibleList<Row: Identifiable, RowContent: View>: View {
  private let rows: [Row]
  private let rowContent: (Row) -> RowContent

  init(rows: [Row], @ViewBuilder rowContent: @escaping (Row) -> RowContent) {
    self.rows = rows
    self.rowContent = rowContent
  }

  var body: some View {
    if #available(iOS 14.0, *) {
      ScrollView {
        LazyVStack(spacing: 0) {
          rowsBody
        }
      }
    } else {
      List {
        rowsBody
      }
      .listStyle(PlainListStyle())
      .introspectTableView { tableView in
        tableView.separatorStyle = .none
      }
    }
  }

  var rowsBody: some View {
    ForEach(rows) { row in
      rowContent(row)
    }
  }
}

While nice note that we are now much more limited in our API. We can't use Section for instance. For that we would need to go one level deeper and make our own custom Result Builder (formerly known as Function Builder).

You may also have noticed that this custom view only handle half of our issues: we don't handle NavigationLink for iOS13 and iOS14! Easiest solution I found is to build an extension to use in place of NavigationLink itself.

extension View {
  @ViewBuilder
  func listItemLink<Destination: View>(_ destination: Destination) -> some View {
    if #available(iOS 14, *) {
      NavigationLink(destination: destination) {
        self
      }
      .frame(maxWidth: .infinity, maxHeight: .infinity)
    } else {
      ZStack {
        NavigationLink(destination: destination) {
          EmptyView()
        }
        .opacity(0)

        self
      }
    }
  }
}

You may not need frame(maxWidth:,maxHeight) but I noticed some scroll performance issues when using NavigationLink inside LazyVStack without it.

Customing a list in SwiftUI on iOS13 is far beyond from simple. On the other hand starting the use of LazyVStack on iOS14 will make it painless. But it will come at the cost of some tradeoffs to support both platforms.

]]>
https://swiftunwrap.com/article/dependency-injection-basicsDependency injection principlesLet's have a look to what is dependency injection and what we need to do to ensure our code is ready for injection before using frameworks like Swinject.https://swiftunwrap.com/article/dependency-injection-basicsTue, 27 Oct 2020 00:00:00 +0000Using dependency inside an app is a nice technique to decouple dependencies and maintain them over time.

But to decouple our code no need for a dependency injection framework! Actually introducing one before decoupling might even have the opposite desired effect...

So today let's have a look to dependency injection principles with no framework and how we can structure our code to ease its use.

Principles

Dependency injection principle is simple: you do dependency injection when giving objects than others rely on either through init or properties. This specific part is also called Inversion of Control: instances are not defined by the class but given to it (but very often Inversion of Control and dependency injection naming are used interchangeably)

Here is an simple dependency injection example where we inject URLSession to our class MovieRepository:

class MovieRepository {
  let urlSession: URLSession

  init(urlSession: URLSession) {
      self.urlSession = urlSession
  }
}

let instance = MovieRepository(urlSession: URLSession())

On the other hand this code is what we would call hardcoded dependencies:

class MovieRepository {
  let urlSession: URLSession

  init() {
      self.urlSession = URLSession()
  }
}

let instance = MovieRepository()

Put it another way dependency injection is merely just that: not hardcoding instances 😳.

To efficiently work a dependency injection framework will actually also rely on another pattern, ServiceLocator.

Opening classes

To be compliant and ready for injection the first thing we have to start with is getting rid of hardcoded dependencies. Depending on your codebase removing them right away might be cumbersome or even error prone. In this case best advice is to take a step smaller and make injection optional:

class MovieRepository {
  let urlSession: URLSession

  init(urlSession: URLSession = URLSession.shared) {
      self.urlSession = urlSession
  }
}

let instance = MovieRepository()

Here we provide a default value for urlSession. It allow us to open our class to injection while still maintaining backward compatibility by providing a default (hardcoded) dependency. Doing so we will be able to migrate little by little our classes without breaking the whole project before moving to a dependency injection framework.

Fixing relationships

Opening our classes for injection is however not always that easy. Let's take this code for instance:

class MovieViewModel {
  let repository: Repository

  init(urlSession: URLSession, jsonDecoder: JSONDecoder) {
    self.repository = Repository(urlSession: urlSession, jsonDecoder: jsonDecoder)
  }
}

class Repository {
  let network: Network

  init(urlSession: URLSession, jsonDecoder: JSONDecoder) {
    self.network = Network(urlSession: urlSession, jsonDecoder: jsonDecoder)
  }
}

class Network {
  init(urlSession: URLSession, jsonDecoder: JSONDecoder) {
    self.urlSession = urlSession
    self.jsonDecoder = jsonDecoder
  }
}

let viewModel = MovieVidewModel(urlSession: URLSession(), jsonDecoder: JSONDecoder())

Let's see what going on here. Each object has its own set of dependencies:

  • Network depend on URLSession and JSONDecoder
  • Repository depend on Network
  • Finally, MovieViewModel depend on Repository

Because MovieViewModel depend on Repository it also implicitly depend on Repository dependencies. These are called transitive dependencies.

If we look to our code we can see that one object dependencies (Network) is carried over the others (MovieViewModel, Repository). As such MovieViewModel and Repository will (strongly) depend not only on their own dependencies but also on their transitive ones.

This approach come with multiple downside:

  • 🐫 Redundancy: you will pass same object instance at multiple places
  • 🏗️ Complex refactoring: multiple classes will be impacted when changing one class dependencies. This is a cascade effect.
  • 🔊 Noise: a class like MovieViewModel does not use URLSession nor JsonDecoder yet we still have to pass them at init.

Good luck to use a dependency injection framework on top of that 🤼.

To fix that all we need to do is clean our init methods and only give direct dependencies to our classes. In other word: MovieViewModel and Repository must not reference their transitive dependencies but only their own dependencies. Once we've done that we can then apply default value technique we saw before.

class MovieViewModel {
  let repository: Repository

  init(repository: Repository) {
    self.repository = repository
  }
}

class Repository {
  let network: Network

  init(network: Network) {
    self.network = network
  }
}

class Network {
  init(urlSession: URLSession = .share, jsonDecoder: JSONDecoder = JSONDecoder()) {
    self.urlSession = urlSession
    self.jsonDecoder = jsonDecoder
  }
}


let viewModel = MovieVidewModel(
    repository: Repository(
        network: Network()
    )
)

Our code relationships are now clean and we are now ready to use a dependency injection framework.

Conclusion

Even if you don't intend to make dependency injection in your app a goal, respecting your object relationships/dependencies help reducing coupling and as such code complexity.

It will also increase your code readability by explicitly stating objects relationships. On the other hand it will probably make instance creation a little bit more verbose. That's where dependency injection frameworks might come in handy.

]]>
https://swiftunwrap.com/article/url-components-encodingURLComponents encodingSince iOS8 a new class for building urls made it to Foundation: URLComponents. While convenient it has some subtilities about encoding that you should be aware of.https://swiftunwrap.com/article/url-components-encodingTue, 15 Sep 2020 00:00:00 +0000Let's say we're working on a phone app and we need to query an API to get data about a phone number. As it is working worldwide we need to use international format +<countryCode><countryNumber>. Thanks to URLComponents our query can built as below:

var builder = URLComponents(string: "https://smsservice.com")!

builder.path = "/status"
builder.queryItems = [URLQueryItem(name: "number", value: "+33601020304")]

let requestURL = builder.url

Pretty simple and easy to read 💪. We now send our request to our server and get... a 400 http response 😱!

The devil is in the detail

The reason is pretty simple actually and lies into URLComponents documentation. It explicitly state that queryItems (the ones storing our phone number) are encoding along RFC 3986 🧨. Never heard about it? Put it simply it is a standard where plus sign is a valid query character meaning it does not need to be encoded.

Servers on the other hands usually follow URIs, URLs, and URNs: Clarifications and Recommendations 💣. And guess what? In this recommendation plus sign is a synonym for spacing in query meaning ?greeting=hello world and ?greeting=hello+world are the same thing.

As a result our server didn't see our number as "+33601020304" but actually as " 33601020304" 💥. To have our plus sign recognised by our server as such we have to manually encode it.

Same player play again

First we can do is defining a custom encoding function on URLQueryItem and use it when adding ou query parameters:

extension URLQueryItem {
    func percentEncoded() -> URLQueryItem {
        /// addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) encode parameters following RFC 3986
        /// which we need to encode other special characters correctly.
        /// We then also encode "+" sign with its HTTP equivalent

        var newQueryItem = self
        newQueryItem.value = value?
            .addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)?
            .replacingOccurrences(of: "+", with: "%2B")

        return newQueryItem
    }
}

extension Array where Element == URLQueryItem {
    func percentEncoded() -> Array<Element> {
        return map { $0.percentEncoded() }
    }
}
builder.queryItems = [URLQueryItem(name: "number", value: "+33601020304")].percentEncoded()

print(builder.url)

But that's not enough as queryItems automatically encode each item which is what we usually want. Leading to double encoding special characters. To "disable" default behaviour we actually have to use another attribute:

builder.percentEncodedQueryItems = encodedQueryItems

print(builder.url)

And voila! You now are able to send a valid international number to your server.

]]>
https://swiftunwrap.com/article/advanced-fastlaneEnvironment variables with Dotenv in FastlaneFastlane is a very famous and useful tool but do you know how to use effective environments within it? Let's have a look!https://swiftunwrap.com/article/advanced-fastlaneTue, 25 Aug 2020 00:00:00 +0000You probably know and use Fastlane. Over the years it became de-facto continuous delivery tool for iOS platform. But do you know about one of its most secret and yet powerful feature: dotenv?

This is an adaptation of a former version I wrote some years ago in French 🇫🇷

dotenv

dotenv is a ruby gem allowing to handle multiple environments using dot files. And guess what? Fastlane come with built-in support through --env parameter:

fastlane distribute --env prod

Using --env option Fastlane/dotenv will load following files, adding defined values into ENV environment:

  • .env
  • .env.default
  • .env.<env-option> (in our example, .env.prod)

If a value is defined into multiple files then the last one win. That is very useful to define default values for example (more on that later).

Fastlane

Let's use a basic Fastfile with no dotenv. To handle environments we have 2 options:

  1. define a hash with values per environment
  2. define one line per environment (distribute**)
my_environment[:prod] = {
  :SLACK_URL => '...',
  :CRASHLYTICS_KEY => '...',
  :SCHEME => '...',
  ...
}

my_environment[:dev] = {
  :SLACK_URL => '...',
  :CRASHLYTICS_KEY => '...',
  :SCHEME => '...',
  ...
}

before_all do |options|
  env = // determine environment one way or another
end

lane :distribute do
  gym(my_environment[env])
  appstore() if env == "prod"
  firebase() if env == "dev"
end
lane distribute_prod do |options|
  distribute(...)
  appstore()
end

lane distribute_dev do |options|
  distribute(...)
  firebase()
end

private_lane distribute do |options|
  distribute(...)
end

Both versions are pretty verbose and hide a little bit what we're trying to actually do (declare a distribute lane). Let's try to migrate this Fastfile to use dotenv and compare the result.

dotenv + Fastlane

We can first move common values into .env.default file. This file is the perfect place for values common across all environments or default values which may change from time to time in some environments.

// .env.default
SLACK_URL = "..."

CRASHLYTICS_KEY = "..."

You can also use .env file instead of .env.default or both of them.

We can then move environment variables into their dedicated files:

// .env.preprod
GYM_SCHEME = "Scheme-Preprod"
GYM_EXPORT_METHOD = appstore
UPLOAD_PLATFORM = "testflight"

// .env.dev
GYM_SCHEME = "Scheme-Dev"
GYM_EXPORT_METHOD = development
UPLOAD_PLATFORM = "firebase"

Last but not least our Fastfile now look as above:

lane :distribute do
  gym()
  testflight() if ENV['UPLOAD_PLATFORM'] == 'testflight'
  firebase() if ENV['UPLOAD_PLATFORM'] == 'firebase'
end

Pretty simple! 👌

You may have noticed we didn't pass any parameter to gym: Fastlane lanes have actually native support for configuration through ENV. Gym will automatically use GYM_SCHEME and GYM_EXPORT_METHOD values! Which make our Fastfile even lighter! 💪

To know which environment variables you can use with a lane take a look at its documentation.For instance fastlane action gym will show supported environment variables for Gym.

Let's recap

Using dotenv we gained:

  1. 🤏A smaller Fastfile: less lanes, no environment code/hash
  2. 💯Extensibility: adding a new environment is just adding a dot file. No impact on our Fastfile
  3. 📖Flexible configuration: as already stated, lanes have support for configuration through environment. This open wide possibilities
  4. 🚀A generic Fastfile! Want to reuse it across multiple projects? Go ahead! That's actually what I've been doing for quite some time with my own one

We however may have lost a little bit explicitness because of the auto-magically use of the ENV variables. I think the tradeoff is worth it though as they can still be used explicitly if needed/preferred or some sort of guard can be added at the very beginning of lanes.

]]>
https://swiftunwrap.com/article/binding-swiftui-previewCreate a mutable @Binding in SwiftUI PreviewsLet's see a small tip helping us having a Binding which can change over time when interacting with a SwiftUI preview.https://swiftunwrap.com/article/binding-swiftui-previewTue, 11 Aug 2020 00:00:00 +0000If you played a little bit with SwiftUI chances are you may have wondered about one thing in preview: how to make a real @Binding that can be modified?

struct TalkDetail_Previews: PreviewProvider {
    struct var previews: some View {
        TalkDetail(.constant(Preview.sampleTalk)) // I'm able to make a Binding but actions won't take effect
    }
}

.constant is not what we are looking for here as we want to make modifications to the data. Binding(get:set:) might not be a good fit either: it won't trigger any state update and so no view update. The trick here is to make a in-between view which will store our data and send it back to the view we are testing:

/// Store a (writable) binding transferred to underlying content
struct WithBinding<T, Content: View>: View {
    @State private var data: T
    private let content: (Binding<T>) -> Content

    init(for data: T, @ViewBuilder content: @escaping (Binding<T>) -> Content) {
      self.data = data
      self.content = content
    }

    var body: some View {
      content($data)
    }
}

You can now change your preview and have your binding working as expected 🚀

struct TalkDetail_Previews: PreviewProvider {
    static var previews: some View {
        WithBinding(for: Preview.sampleTalk) {
            TalkDetail($0)
        }
    }
}
]]>
https://swiftunwrap.com/article/separation-of-concern-viewmodelViewModels and Separation of ConcernI see many developers adding too many code inside their ViewModel ending with a Massive ViewModel 🦛 while one of the first reason to use it was to actually avoid... Massive ViewController 🤷. Why is it happening? I think it is coming from one simple reason: they forget to think about Separation of Concern.https://swiftunwrap.com/article/separation-of-concern-viewmodelTue, 2 Jun 2020 00:00:00 +0000I see many developers adding too many code inside their ViewModel ending with a Massive ViewModel 🦛 while one of the first reason to use it was to actually avoid... Massive ViewController 🤷.

Why is it happening? I think it is coming from one simple reason: they forget to think about Separation of Concern.

Let's build a (simplified) ViewModel handling a book that can be refreshed and ordered.

class BookViewModel {
  let book: BehaviorRelay<Book>

  private let refreshAction: (Book) -> Future<Book>
  private let addToBasketAction: (Book) -> Future<Bool>

  func refresh() {
    refreshAction().mapself.book = $0 }
  }

  func addToBasket() {
    addToBasket(book.value)
  }
}

So far so good. Now our view need to display book information right? So let's just add that:

class BookViewModel {
  let book: BehaviorRelay<Book>
  private(set) lazy var title = book.map(\.title)
  private lazy var publishedAtFormatter: DateFormatter  {
    // build a date formatter
  }
  private(set) lazy var publishedAt = book.map(\.publishedAt).map(self.publishedAtFormatter.string(from:))
  //... and so on

  func refresh() { ... }
  func addToBasket { ... }
}

And we'll now use those properties in our views. Great. But we just did something wrong: we broke our Separation of Concern.

Separation of Concern

In most apps you'll find at least those three layers 🍰:

  • 📱Presentation, that's everything about UI
  • 💼Business (or domain), all the stuff related to ruling your app
  • 💾Data, technical layer to access data

Wether your app/product is using those three layers or others the rule is the same: to respect SoC (Separation of Concern) any class should lie into exactly one layer.

To which layer does our BookViewModel belong to? Well according to the first version we did it should belong to Business layer as it is dealing with a Book and some other related actions. But the second version actually added relationships with Presentation layer as well. Our ViewModel will now change whenever one of those two layers will have new requirements:

  • Need to display more information to user? Or to change formatting? Our ViewModel will change
  • Need to change some rules about how to added to basket ? Our ViewModel will change.

This has many drawbacks, the biggest ones being:

  • We're getting a Massive ViewModel 🦛: we add code for two layers
  • It's hard to test. We'll have to mock/stub a lot of things to test unrelated things. Here for example we'll have to mock refreshAction and addToBasketAction while testing publishedAt formatting. Yet the two are not related.

Now I must admit ViewModel naming is maybe not the smartest one in the world making its definition not clear: is it a model handling a view? Or its data? You'll have to choose between the two.

ViewState

In this pattern ViewModel is acting like in our first example: it is handling domain attributes and perform any necessary actions. It mean you should have very few properties inside it and all of them domain oriented. Every stuff related to Presentation will either lie into a ViewController or some dedicated classes. For instance here is how I usually bind ViewModel to the UI:

class BookViewController: UIViewController {
  func bind() {
    bookViewModel.book.map(\.title).bind(to: view.titleLabel.rx.text).disposed(by: bag)
    bookViewModel.book.map(\.publishedAt).map(DateConverter.string).bind(to: view.publishedAt.rx.text).disposed(by: bag)
    view.addToBasketButton.rx.tap.subscribe(onNext: { [viewModel] in viewModel.addToBasket() }).disposed(by: bag)
  }
}

enum DateConverter {
  static func string(_ date: Date, locale: Locale = .current) -> String {
    // any needed formatting
  }
}

With this SoC not only is my BookViewModel light it also has one clear and defined goal: to provide a domain api to my view. Everything else is handled by the view itself. Using these Converter classes also achieve having a reusable and testable code.

ViewData

Here ViewModel is quite dumb 🙃. It is no more a domain model but actually just a mapper for the view. It is either exposing a 1-1 mapping for the view or it is doing the mapping directly (basically the bind method we defined just before).

class BookViewData {
    private let book: Book
    private formatter: DateFormatter

    var title: String { book.title }
    var publishedAt: String { formatter.string(from: book.publishedAt) }
}

In any case you will still need someone to get the models used by those ViewData and perform the actions. People seem to often call them Interactor : they will more or less have the same role as our ViewState classes.

ViewState or ViewData?

I do prefer seeing ViewModel as the view state so I would recommend you to stick to ViewState pattern.

Moreover the view stuff can be splitted and reuse across the whole when needed using Converter which make me feel that ViewData is an unnecessary level of abstraction. But in the end the most important is to make a choice, so choose one and stick to it into your whole application!

]]>
https://swiftunwrap.com/article/modeling-done-rightHandling relationships in modelsModels are the core of our applications and need to be defined with care. Yet they very often transform themselves into big monolithics becoming more and more complex while being less and less easy to use over time. But why is that?https://swiftunwrap.com/article/modeling-done-rightTue, 28 Apr 2020 00:00:00 +0000Models are the core of our applications and need to be defined with care. Yet they very often transform themselves into big monolithics becoming more and more complex while being less and less easy to use over time. But why is that?

Let's say we're working on a e-commerce app where people can buy products and leave comments on them. Modeling this should be easy, right?

struct Product {
  let id: Int
  let name: String
  let description: String
  let price: Price
}

struct Comment {
  let id: Int
  let createdAt: Date
  let text: String
  let product: Product
}

At first glance we might think our modeling is correct: we have information about the Comment itself along with the Product it is related to.

Now let's say we can select a product want to show a bunch of its comments:



func getComments(of product: Product) -> Future<[Comment]> {
  ... load comments
}

We might find our first issue here. If you're using a REST API, chances are the API will return a list of comments but not the related product. In that case you may need to make map comments with the product before returning it:

func getComments(of product: Product) -> Future<[Comment]> {
  return builder
    .query(.comments(of: product))
    .request()
    .map { Comment(id: $0.id, createdAt: $0.createAt, text: $0.text, product: product) }
}

We now have our Comment objects. Expect our Product is repeated on each of them. Not really useful as it is going to be the same every time. It also introduce a subtle yet real "vulnerability" into our code: we can have a list of Comment having different Products. Which is not what we are looking for 😕.

Perhaps the first workaround most people will think about is reversing relationship by making Comment a child of Product:

struct Product {
  let id: Int
  ...
  let comments: [Comment]
}

Using this technique we indeed don't repeat Product for each Comment! 👏 However if we take a closer look we see a new issue: we need comments to get our product 😨.

We may decide to just stick to an empty array and load comments only when necessary. While working it would yet introduce new issues or questions:

  1. How to differentiate between a product with no comments and a product whose comments have not been loaded yet?
  2. How about a single relationship attribute? Does it mean it would need to be optional even when it should not?
  3. How to reconciliate product and its comments once they are loaded? (our comments are declared with a let)

None of these two solutions work without introducing drawbacks. Bu if neither Comment nor Product can carry the relationship then where should it go?

Breaking the bound

Comment and Product are two distinct objects that sometimes share a relation: the relation exist but we are not always interested in.

Just like a parent and a child: while there is an obvious relationship between the two of them, parents do not always want to carry their child with them. In the meantime the child should be able to live its life without having its parents around him all the time. In other words: relationships should not be what define models.

The correct way to define relationships is actually throughout one (or sometimes many) composition objects: objects whose sole purpose is to make links between other objects.

struct ProductComments {
  let product: Product
  let comments: [Comment]
}

func getComments(of product: Product) -> Future<ProductComments> {
  return builder
    .query(.comments(of: product))
    .request()
    .map { ProductComments(product: product, comments: $0) }
}

Using a composition object (ProductComments) bring a lot of advantages:

  • Product and Comment are way simpler. And they can be used without having to refer/deal with the other one (remember the child without its parents).
  • You avoid having big models with tons of attributes by breaking them into smaller ones. Which otherwise make using them and refactoring them a challenge.
  • Our relations are flattened. This is not visible here but instead of having dive into hierarchy to find an object (a.b.c...) all our relationships are gonna be only one depth (a.*)
  • Last but not least relationships are actually expressed. We are using ProductComments because we are interested into the relationship.

It is nothing new. It is coming from Domain Driven Design where those objects are called Aggregate Roots. In future articles I will show how this modeling improve code isolation when combined with other patterns like Smart/Dumb components.

]]>
https://swiftunwrap.com/article/switching-environmentsSwitching environments in iOSWhen working on your project you often need to switch environments. Whether it is check a bug on production or to use the latest API on your dev server this is a quite common scenario. But doing it properly and easily in iOS has always been challenging. Let's see how we can handle it.https://swiftunwrap.com/article/switching-environmentsTue, 21 Apr 2020 00:00:00 +0000When working on your project you often need to switch environments. Whether it is check a bug on production or to use the latest API on your dev server this is a quite common scenario. But doing it properly and easily in iOS has always been challenging. Let's see how we can handle it.

What is an environment anyway?

Talking about environment in iOS might suggest talking about Schemes, Configurations or Build Settings which are all related subjects. Here environment mean variables used at runtime and which (might) change depending on selected Configuration. Some examples:

  • Your server URL
  • A service credentials
  • Feature toggling

On the other hand these are not environment variables but Build Settings:

  • build number
  • build identifier

The common way: Plist

Before seeing my solution let's have a look at perhaps the most common way of handling environment values in iOS: Plist. Put it simply using Plist can be resumed in four steps:

  1. Create a xcconfig file per Configuration (Debug.xcconfig, ...)
  2. Add custom Build Settings inside it (SERVER_URL, ...)
  3. Add new entries inside Info.plist to reference our custom Build Settings (myServerUrl => $(SERVER_URL))
  4. Define an Environment type to lookup for settings from Info.plist

Let's take a closer look at some of these steps

Custom Build Settings, Oops I mistyped it again

While there is nothing wrong with defining custom Build Settings it is easy to mispell them. Especially here where we define them in one placee (.xcconfig) to use them in another one (Info.plist).

What's the cost? Your code will compile but fail at runtime 😈

You may think it's not that serious. "I'll see it while developing". Most of the time you will. But if you mispelled (or even forgot to declare) the setting in only one Configuration then the issue might been left unnoticed. And guess which environment might indeed have that sort if issue? Yep, production! 😏

// Debug.xcconfig
// APII_KEY? Or maybe was it API_KEY?
APII_KEY = xxx
SERVER_URL = https://swiftunwrap.com

// Release.xcconfig
// Boom, a production app with no SERVER_URL defined! 🚨
API_KEY = xxx

The Environment type is verbose

While writing your Environment type you will may notice that it is kind of verbose compared to what you expect it to do.

public enum Environment {
  static let serverURL: URL = {
    guard let serverURLString = Bundle.main.object(forInfoDictionaryKey: "SERVER_URL") as? String else {
      fatalError("Server URL not set in plist for this environment")
    }
    guard let url = URL(string: serverURLString) else {
      fatalError("Server URL is invalid")
    }
    return url
  }()
}

All that code just to define one attribute making our code hard to read. We may mitigate that issue using some of Swift powers:

public enum Environment {
  static let serverURL: URL = {
    return (Bundle.main
      .requiredObject(forInfoDictionaryKey: "SERVER_URL") as String)
      .asURL()
  }()
}

extension Bundle {
  func requiredObject<T>(forInfoDictionaryKey key: String) -> T {
    guard let value = object(forInfoDictionaryKey: "SERVER_URL") as? T else {
      fatalError("\(key) not set in plist for this environment")
    }

    return value
  }
}

extension String {
  func asURL() -> URL {
    guard let url = URL(string: self) else {
      fatalError("URL is invalid")
    }

    return url
  }
}

It is still quite verbose for one attribute but get better when having multiples. However we don't have any guarantee on our code. object(forInfoDictionaryKey:) return Any? so it is up to you to check and cast the type. Hence our fatalError all around the place.

Did you also notice that after casting into String we are mapping to URL? Why not just making a URL in first place? Because with Plist you are limited in possible types: String, Number, Bool, Array or Dictionary. That's it. Nothing less. Neither URL, tuples nor custom types 🤷‍.

Let's recap. Using Plist we may:

  • mispell/forget a value in .xcconfig or Info.plist
  • mispell the key in our Environment type
  • use the wrong type and have a failing cast
  • limit ourselves to five primtive types

Surely we can do better than that!

The less common way: Swift code

No need for complexity

Let's start by modeling what we want to do. We want to have an environment with some properties:

struct Environment {
  let serverURL: URL
  let apiKey: String
}

We have our model and it is very simple. No extra lines to define values. However question remain whether or not we are able to define an environment per configuration. We actually can:

extension Environment {
  private static let debug = Self(serveurURL: ..., apiKey: ...)
  private static let release = Self(serveurURL: ..., apiKey: ...)
}

With these few lines we just did what we were doing in Build Settings and Plist 💪. How to use them? You either use dependency injection or determine environment at compile time:

extension Environment {
  #if DEBUG
  static let current: Environment = .debug
  #else
  static let current: Environment = .release
  #endif
}

And we're done. Our environment is ready. Compared to previous solution we gained:

  • Concise code. And adding a new environment attribute is just... declaring a new struct attribute 😍
  • All attributes are available at compile time. You can't distribute your app with forgetting one 🎉
  • You can easily use any type we want 🚀
  • Best of all, we only wrote Swift code! 🧁

We do have one drawback however: all our environments are shipped in our application even in production. This actually might be useful in dev because with some code we can add environment switching functionality. But in production we want environment to be frozen.

Excluding environments

To exclude environments we can rely on a non very known Build Setting {INCLUDED/EXCLUDED}_SOURCE_FILE_NAMES that allow to include/exclude files at compile time. Let's split our environments into multiple files and just include the ones we need:

// Common.xcconfig
ENV = $(CONFIGURATION)
// we first exclude all files from Env
EXCLUDED_SOURCE_FILE_NAMES = Env/**/*
// and then re-include only some
INCLUDED_SOURCE_FILE_NAMES = Env/Env.swift Env/$(ENV)/*

We then change the way to determine current environment:

// Env/Env.swift
protocol EnvProvider {
  static var current: Envget }
}

struct Environment: EnvProvider { ... }

// Env/Debug/Debug.swift
extension Env {
  private static let debug = Self(...)
  static let current = .debug
}

// Env/Release/Release.swift
extension Env {
  private static let release = Self(...)
  static let current = .release
}

Let's recap:

  1. We first create our Environment struct
  2. We define once instance per Configuration
  3. We write (once) our include/exclude file

Having used both methods on projects the later one is, from my experience, easier and safer way to manage environments than with Plist. Adding new environment attribute should not only be fast but also not risky. Finally I think it makes more sense for a new developer coming on the project to look to data in Swift code rather than in an obscure Plist file.

Hoping to see many projects to adopt this methodology! 🧑‍🚀🚀

]]>