Swift Struct Initialization, Making Another Struct Like String

Swift struct initialization, making another struct like String

This is (in my opinion) neat part of the language. Yes, this is possible, thanks to the ExpressibleByStringLiteral protocol.

Unfortunately, there is some complexity to it. ExpressibleByStringLiteral inherits from ExpressibleByExtendedGraphemeClusterLiteral, which itself inherits from ExpressibleByUnicodeScalarLiteral. Thus, to conform to the first, you must conform to the other 2 above it.

This makes it possible for your struct or class to be initialized from:

  • A UnicodeScalarLiteralType (such as a UnicodeScalar, which is a single Unicode code point, e.g. "A")
  • An ExtendedGraphemeClusterLiteralType (such as a Character, which is a collection of UnicodeScalars, such as "/code>)
  • A StringLiteralType (such as String, which is a collection of Characters, such as "This is a string")

Here's an example implementation that just sets a String member variable:

struct StringV2: ExpressibleByStringLiteral {
let s: String

init(unicodeScalarLiteral: UnicodeScalar) {
s = String(unicodeScalarLiteral)
}

init(extendedGraphemeClusterLiteral: Character) {
s = String(extendedGraphemeClusterLiteral)
}

init(stringLiteral: String) {
s = stringLiteral
}
}

let s1: StringV2 = "This is a string" // String
print(s1.s)

let s2: StringV2 = "A" // Unicode scalar
print(s2.s)

let s3: StringV2 = " // Extended grapheme cluster
print(s3.s)

How can I initialize a struct or class in Swift?

The anonymous closure is an unnamed function. It's useful whenever you want to initialize something with the result of calling a function, but you don't want to write a function like:

  func makeDan() -> Person {
...
}

let person = makeDan()

That would mean you have to come up with a name for the function and find some place to put it, which means it's also visible and can be called by other pieces of code that don't need anything to do with makeDan. It's often useful to have functions that have no names.

Needing a function to initialise something is useful when you have something complex that needs some kind of computation, so multiple lines of stuff. It's also useful when the initialization is only wanted to be done if/when required:

lazy var dan: Person = { ... }()

Perhaps because the computation is expensive in some kind of resource use, like cpu or memory. Or possibly because the computation involves some kind of side effect, like opening a database or something, that's only wanted if/when the property is used.

Swift setting a struct value within another struct init

The problem is that none of your lines are actually accessing instances of your Cell struct.

Here's a functioning adaptation of your code. I allowed myself to remove extra stuff that seem to have been left out from your codebase:

struct Cell {
var position: (Int,Int)

init(_ position: (Int,Int)) {
self.position = (0,0)
}
}

func positions(rows: Int, cols: Int) -> [(Int, Int)] {
return (0 ..< rows)
.map { zip( [Int](repeating: $0, count: cols) , 0 ..< cols ) }
.flatMap { $0 }
.map { ($0.0, $0.1) }
}

struct Grid {
var rows: Int = 10
var cols: Int = 10
var cells: [[Cell]] = [[Cell]]()

init(_ rows: Int, _ cols: Int) {
self.rows = rows
self.cols = cols
self.cells = Array.init(repeating: Array.init(repeating: Cell((0,0)), count: cols), count: cols)

positions(rows: rows, cols: cols).forEach { row, col in
cells[row][col].position = (row, col)
}
}
}

let g = Grid(1, 2)
print(g.cells[0][1].position)

Now, for a more detailed explanation of the errors you encountered:

var position = cells(position: (row, col))

Here you're not setting anything on any cell. Instead, you're trying to call your grid as if it was a function, with a parameter position: (Int, Int).

cells.position = (row, col)

Here you're trying to set a property position on your matrix ([[Cell]]). And obviously, Swift complains that such property does not exists in its builtin type Array.

cells.position(row, col)

Here you're trying to set a property position on your matrix ([[Cell]]) and call it as a function with two parameters Int. The problem is similar as above.

position *= cells.position(row, col)

Here I'm can't tell what's going on, since position does not seems to have been declared in your code. I guess it comes from elsewhere in your codebase, or maybe it's merely a typo.

Setting type of generic struct during initialization (Swift)

All you have to do is to tell the compiler that T is a String

init(fromString: String, separator: String) where T == String {
// ...
}

How to automatically create an initializer for a Swift class?

Update As of Xcode 11.4

You can refactor (right-click mouse menu) to generate the memberwise initializer for class and struct.

Note that struct automatic initializers are internal. You may want to generate memberwise initializer when defining a module to make it public.

Right-click > Refactor > 'Generate Memberwise Initializer'

xcode generate memberwise initialization

For older Xcode

There are handy plugins:

https://github.com/rjoudrey/swift-init-generator https://github.com/Bouke/SwiftInitializerGenerator



Related Topics



Leave a reply



Submit