Working with Arrays in Swift
Handle edge cases

If the given numbers array is empty then the first loop will do nothing, but

computedNumbers.append(numbers[count - 1] * numbers[0])


aborts with a runtime exception. The correct result in this case would be to set computedNumbers to an empty array as well.

Better loops

This

var i = 0
while i &lt; count - 1 {
    // ...
    i = i + 1
}


is shorter and better done as

for i in 0 ..&lt; count - 1 {
    // ...
}


The advantages are


The scope of i  is limited to the loop.
i is a constant.
The range of i is clearly seen right at the start of the loop.


In this particular case we iterate of adjacent array elements, which is conveniently by by zip()ping the array with a shifted view of itself:

var computedNumbers = zip(numbers, numbers.dropFirst()).map(*)
computedNumbers.append(numbers[count - 1] * numbers[0])


If we append the first element to the shifted view then the complete operation reduces to

let computedNumbers = zip(numbers, [numbers.dropFirst(), numbers.prefix(1)].joined()).map(*)


Note that this handles also the case of an empty array gracefully.

Use constants if possible

The numbers array is never mutated, therefore it should be declared as a constant with let:

let numbers = [3, 1, 4, 2]


Using a constant 


makes it clear to the reader that this value is never mutated,
helps to avoid unintentional mutation,
possibly allows better compiler optimization.


Make it a function

If we put the functionality in a separate function then


it becomes reusable,
test cases can be added more easily,
we can give it a meaningful name,
we can add documentation.


In our case the function would simply be

/// Compute an array where each element in the numbers array is multipled by
/// the next following element. The last element is multiplied by the first element.
/// - Parameter numbers: An array of integers.
func circularAdjacentProducts(of numbers: [Int]) -&gt; [Int] {
    return zip(numbers, [numbers.dropFirst(), numbers.prefix(1)].joined()).map(*)
}


and would then be used as

let numbers = [3, 1, 4, 2]
let computedNumbers = circularAdjacentProducts(of: numbers)
print(computedNumbers)


Make it generic on the element type

The same operation could be done with an array of floating point values (Float or Double), or with other integer types (e.g. Int16) as long as the multiplication does not overflow. Therefore we can generalize it to array of type [T] where T conforms to a protocol which required a multiplication operation.

A possible choice is the Numeric protocol which is adopted by all floating point and integer types:

func circularAdjacentProducts&lt;T: Numeric&gt;(of numbers: [T]) -&gt; [T] {
    return zip(numbers, [numbers.dropFirst(), numbers.prefix(1)].joined()).map(*)
}


Example:

let floats = [3.3, 1.1, 4, 2.2]
let computedFloats = circularAdjacentProducts(of: floats)
print(computedFloats) // [3.63, 4.4, 8.8, 7.26]


Make it generic on the collection type

The next possible generalization is to apply this operation not only to arrays, but to arbitrary collections:

extension Collection where Element: Numeric {
    func circularAdjacentProducts() -&gt; [Element] {
        return zip(self, [dropFirst(), prefix(1)].joined()).map(*)
    }
}


Example with an ArraySlice:

let numbers = Array(1...10).dropFirst(3)
print(numbers) // [4, 5, 6, 7, 8, 9, 10]
let computedFloats = numbers.circularAdjacentProducts()
print(computedFloats) // [20, 30, 42, 56, 72, 90, 40]