Swift Array handling of Subclasses - objective-c

If I have a variable declared like var gameBoard: [Piece] = [], is there any way to add a subclass of Piece, called Queen, to the array?
I am using Piece to represent all pieces. Queen, Pawn, Bishop and such are all subclasses of Piece, and should be included on the board.
I remember doing this frequently in Objective C, where subclasses were able to be used in place of the superclass. But in my first attempts, it I am getting an error saying
'#lvalue $T11' is not identical to 'Piece`
Is this not possible anymore? Or would there need to be some use of generics that I cannot think of right now?
Edit
Here is the implementation of my board, including only the relevant parts.
struct GameBoard{
var board: [[Piece]]
init() {
board = []
for _ in 0...7{
var collumn: [Piece] = []
for _ in 0...7{
var piece = Piece(player: .None, board: self)
collumn.append(piece)
}
board.append(collumn)
}
}
subscript(coords:(Int, Int) ) -> Piece {
return board[coords.1][coords.0]
}
}
The code that fails is
var board = GameBoard()
var q = Queen(player: .Black, board: board)
board[(4,5)] = q //Throws the error
board.board[5][4] = q //Works
It seems to me that these two should work the same way. It may be an issue with the subscripting, but I am not completely sure.

Just to followup on your edits, this works fine in Swift. For example:
class Piece {}
class Bishop : Piece {}
let pieces: [Piece] = [Bishop()]
Do you have an example that does not work?
As a note, when you see #lvalue $T## in your errors, it often means you're trying to modify a constant. An easy way to make that mistake is to try to modify an array that was passed to you and that you did not mark var. For example, see Swift function that takes in array giving error: '#lvalue $T24' is not identical to 'CGFloat'.

Here is how I would write it. Includes answer to the question about subscripting, and for bonus, uses a Coords struct in place of tuples (allows implementation of Printable for example) and uses optionals for each square (allows nil to be used as the representation of an empty square.)
class Piece : Printable {
var description: String { get { return "Piece" } }
}
class Queen : Piece {
override var description: String { get { return "Queen" } }
}
struct Coords : Printable {
let column: Int
let row: Int
init(_ column: Int, _ row: Int) {
self.column = column
self.row = row
}
var description: String { get { return "Coords(\(column), \(row))" } }
}
struct GameBoard {
var board: [[Piece?]]
init() {
board = []
for _ in 1...8 {
board.append(Array<Piece?>(count: 8, repeatedValue:nil))
}
}
subscript(c: Coords) -> Piece? {
get {
return board[c.column][c.row]
}
set (newValue) {
board[c.column][c.row] = newValue
}
}
}
func testGameBoard() {
var board = GameBoard()
board[Coords(4, 5)] = Queen()
func printSquare(coords: Coords) {
if let p = board[coords] {
println("At \(coords) is a \(p)")
} else {
println("At \(coords) is an empty square")
}
}
printSquare(Coords(4, 5)) // Prints: At Coords(4, 5) is a Queen
printSquare(Coords(4, 6)) // Prints: At Coords(4, 6) is an empty square
}

As long as Queen is a subclass of Piece then there would be no problem. As such:
class Piece {}
class Queen : Piece {}
class Board {
var board : [[Piece?]] =
{ var board : [[Piece?]] = []
for _ in 0..<8 { board.append (Array<Piece?>(count: 8, repeatedValue:nil)) }
return board } ()
subscript (row:Int, col:Int) -> Piece? {
get { return board[row][col] }
set { board[row][col] = newValue }}
}
and then use as:
23> let board = Board()
 24> b[0,4] = Queen()
 25> b[7,4] = Queen()
 26> b
$R0: Board = {
  board = 8 values {
    [0] = 8 values {
      [0] = nil
      [1] = nil
      [2] = nil
      [3] = nil
      [4] = Some
      [5] = nil
      [6] = nil
      [7] = nil
    } ...

Related

Picture Storage as URL in SQLite DB in Swift 5

I have a swift project that uses an array of pictures. The array of pictures is kept in storage in the filmanager.default.urls path. The URLs are then kept in a SQLite database. I am able to add to the array without a problem, however, I run into an issue whenever I try to recall it from memory. I do not know if I am saving it wrong or loading it wrong. I will post the code for how I did that here.
func save(images: Array<UIImage>, identifier: String) -> Array<String> {
var picCounter = 0
var URLs: Array<String> = []
for pic in images {
let id = identifier + "makeSureThisCantAccidentallyBeAnID" + String(picCounter)
let jpgImageData = pic.jpegData(compressionQuality: 0.5)
let documentURL = fileManager.urls(for: .documentDirectory, in: FileManager.SearchPathDomainMask.userDomainMask).first
let path = documentURL!.appendingPathComponent(id + ".png")
do {
try jpgImageData!.write(to: path)
} catch {
print(error)
}
picCounter += 1
URLs.append(path.absoluteString)
}
return URLs
}
That is my save function. It should store the URLs and return an array of URLs in string format.
Here is my loading function, it should take an array of URLs and return the array of images.
func recover(URLarray: Array<String>, identifier: String) -> Array<UIImage> {
if URLarray[0] == "fake url" {
let pic = #imageLiteral(resourceName: "LaunchScreen")
let fakeArray = [pic]
return fakeArray
}
var picCounter = 0
var imageArray: Array<UIImage> = []
for url in URLarray {
//let fileData = FileManager.default.contents(atPath: url)
guard let image = UIImage(contentsOfFile: url) else { return imageArray }
picCounter += 1
imageArray.append(image)
}
return imageArray
}
The "#imageliteral" is a literal image that is used instead of an array if there is no array to load. This should theoretically recall every image without returning nil. Lastly, I will post my SQLite saving function
func saveNote(note: Note) {
connect()
var statement: OpaquePointer? = nil
if sqlite3_prepare_v2(
database,
"UPDATE remember SET person = ?, memories = ?, imageurl = ? WHERE rowid = ?",
-1,
&statement,
nil
) == SQLITE_OK {
sqlite3_bind_text(statement, 1, NSString(string: note.person).utf8String, -1, nil)
sqlite3_bind_text(statement, 2, NSString(string: note.memories).utf8String, -1, nil)
var imageURL: String = ""
for url in note.imageURLs {
imageURL.append(url)
imageURL.append("#")
}
sqlite3_bind_text(statement, 3, NSString(string: imageURL).utf8String, -1, nil)
sqlite3_bind_int(statement, 4, note.id)
if sqlite3_step(statement) != SQLITE_DONE {
print("Error saving note")
}
}
else {
print("Error creating note update statement")
}
sqlite3_finalize(statement)
}
This is how I tried to save it. I had to store a URL array as one string so I decided to do so by making it a string I could split with the character "#" if you think that is the problem then please tell me what character or method I could use to do this, as I have tried changing the character. If anyone helps me this far then I am extremely grateful! Thank you!

SwiftUI: Is it possible to automatically move to the next textfield after 1 character is entered?

I trying to make a SwiftUI app where after entering one letter in a TextField the cursor automatically moves to the next TextField. The UI is pretty much like this.
In Swift/IB, it looks like this was done with delegates and adding a target like in this post:
How to move to the next UITextField automatically in Swift
But can't find any documentation for using delegates/targets in SwiftUI.
I tried following this post:
SwiftUI TextField max length
But this has not worked for me. Setting the .prefix(1) does not seem to make a difference. The TextField still accepts any amount of characters and when moved to the next TextField does not reduce the characters entered to only the first character.
In SwiftUI's current state, is it possible to automatically move to the next TextField after 1 character is entered?
Thanks for any help!
It can be done in iOS 15 with FocusState
import SwiftUI
///Sample usage
#available(iOS 15.0, *)
struct PinParentView: View {
#State var pin: Int = 12356
var body: some View {
VStack{
Text(pin.description)
PinView(pin: $pin)
}
}
}
#available(iOS 15.0, *)
struct PinView: View {
#Binding var pin: Int
#State var pinDict: [UniqueCharacter] = []
#FocusState private var focusedField: UniqueCharacter?
var body: some View{
HStack{
ForEach($pinDict, id: \.id, content: { $char in
TextField("pin digit", text:
Binding(get: {
char.char.description
}, set: { newValue in
let newest: Character = newValue.last ?? "0"
//This check is only needed if you only want numbers
if Int(newest.description) != nil{
char.char = newest
}
//Set the new focus
DispatchQueue.main.async {
setFocus()
}
})
).textFieldStyle(.roundedBorder)
.focused($focusedField, equals: char)
})
}.onAppear(perform: {
//Set the initial value of the text fields
//By using unique characters you can keep the order
pinDict = pin.description.uniqueCharacters()
})
}
func setFocus(){
//Default to the first box when focus is not set or the user reaches the last box
if focusedField == nil || focusedField == pinDict.last{
focusedField = pinDict.first
}else{
//find the index of the current character
let idx = pinDict.firstIndex(of: focusedField!)
//Another safety check for the index
if idx == nil || pinDict.last == pinDict[idx!]{
focusedField = pinDict.first
}else{
focusedField = pinDict[idx! + 1]
}
}
//Update the Binding that came from the parent
setPinBinding()
}
///Updates the binding from the parent
func setPinBinding(){
var newPinInt = 0
for n in pinDict{
if n == pinDict.first{
newPinInt = Int(n.char.description) ?? 0
}else{
newPinInt = Int(String(newPinInt) + n.char.description) ?? 0
}
}
pin = newPinInt
}
}
//Convert String to Unique characers
extension String{
func uniqueCharacters() -> [UniqueCharacter]{
let array: [Character] = Array(self)
return array.uniqueCharacters()
}
func numberOnly() -> String {
self.trimmingCharacters(in: CharacterSet(charactersIn: "-0123456789.").inverted)
}
}
extension Array where Element == Character {
func uniqueCharacters() -> [UniqueCharacter]{
var array: [UniqueCharacter] = []
for char in self{
array.append(UniqueCharacter(char: char))
}
return array
}
}
//String/Characters can be repeating so yu have to make them a unique value
struct UniqueCharacter: Identifiable, Equatable, Hashable{
var char: Character
var id: UUID = UUID()
}
#available(iOS 15.0, *)
struct PinView_Previews: PreviewProvider {
static var previews: some View {
PinParentView()
}
}

Swift: Alternatives to super class methods to generate objects

I have the following Objective-C code I'm trying to convert to swift:
-(id)initWithBook:(NSString*)bookTitle author:(NSString*)author description:(NSString*)description{
self = [super init];
if (self) {
self.bookTitle = [bookTitle copy];
self.author = [author copy];
self.description = [uri description];
}
return self;
}
+(NSArray*)listOfBooks:(NSArray*)jsonWithBooks{
NSMutableArray *elements = [NSMutableArray new];
for (NSDictionary *dictElment in jsonRespnse){
Books *booksData = [[Books alloc] initWithBook:[dictElment objectForKey:#"bookTitle"]
title:[dictElment objectForKey:#"author"]
description:[dictElment objectForKey:#"description"]];
[elements addObject:booksData];
}
return [NSArray arrayWithArray:elements];
}
In my Objective-C code I'm calling super class "+(NSArray*)listOfBooks:(NSArray*)jsonWithBooks" to generate NSArray of objects. But I haven't found an equivalente on Swift. Any of you knows what would be the best alternative to do something like this?
I'm trying to use #Alexander example but my project crash in the following line:
let inventoryBooks = Book.books(fromDictArray: json .object(forKey: "books") as! [[String : String]] )
I check the type for this:
json .object(forKey: "books")
As follow:
let arrayOfBooks = json .object(forKey: "books")
if arrayOfBooks is NSArray {
print("nsarray")
}
if arrayOfBooks is [[String:String]] {
print("string:string")
}
if arrayOfBooks is NSDictionary {
print("NSDic")
}
And is printing nsarray
My question. What I'm doing wrong or do I need to change the signature on this function:
static func books(fromDictArray array: [[String: String]]) -> [Book?] {
return array.map(Book.init)
}
This sample of the json response:
{
books = (
{
caption = "";
"display_sizes" =(
{
name = thumb;
uri = "https://someUrl.com/img.jpg";
}
);
id = 123;
"max_dimensions" = {
height = 4912;
width = 7360;
};
title = "Learning Swift";
author = "Some guy"
}
{
caption = "";
"display_sizes" =(
{
name = thumb;
uri = "https://someUrl.com/img.jpg";
}
);
id = 123;
"max_dimensions" = {
height = 4912;
width = 7360;
};
title = "Swift";
author = "me meme"
}
)
}
Here is how I would write this code in idiomatic Swift:
struct Book {
let title: String
let author: String
let description: String
/* an implicit member wise initializer is generated,
which would otherwise look something like this:
init(title: String, author: String, description: String) {
self.title = title
self.author = author
self.description = description
} */
}
// Initialization from Dictionaries
extension Book {
init?(fromDict dict: [String: Any]) {
guard
let title = dict["bookTitle"] as? String,
let author = dict["author"] as? String,
let description = dict["description"] as? String
else { return nil }
self.init(
title: title,
author: author,
description: description
)
}
static func books(fromDictArray array: [[String: Any]]) -> [Book?] {
return array.map(Book.init)
}
}
Here are some notable points:
Book is a struct. Such a broad description of a book doesn't need to support the notion of identity. I.e., your book named "Harry Potter", by "J.K. Rowling" with the description "Some description" can be considered to be the same as my book with the same values. There's no apparent need (yet) to distinguish the identity of your book vs the identity of mine.
Book has an implicit memberwise initializer init(title:author:description:) which simply initializes its fields to the given parameters.
An extension is made which compartmentalizes all dictionary related tasks into a single unit.
A failable initializer, init?(fromDictArray:) is made, which returns a new book based off the given dict (presumably created from your JSON). This initializer is fault tolerant. If the dict provided is invalid, then the initializer will simply return nil, without crashing your program.
A static method is made on the Book struct, books(fromDictArray:), which will create an array of optional books ([Book?], a.k.a Array<Optional<Book>> out of the given dict. It is then the job of the consumer of this method to deal with the nil values, those resulting from invalid dicts, as they please.
They could ignore the nil books:
let books = Book.books(fromDictArray: myDictArray).flatMap{$0}
They could crash if a nil book is found:
let books = Book.books(fromDictArray: myDictArray) as! [Book]
Or they can handle the nil cases in some unique way:
let books = Book.books(fromDictArray: myDictArray).map{ book in
if book == nil {
print("A nil book was found")
}
}
As already mentioed by Dan, the Swift equivalent would be a class func:
class func listOfBooks(jsonWithBooks: [NSDictionary]) -> [Book] {
var books = [Book]()
for json in jsonWithBooks {
let book = Book(
book: json["bookTitle"]!,
author: json["author"]!,
description: json["description"]!
)
books.append(book)
}
return books
}

Using Jastor to translate JSON/NSDictionary to Typed Swift classes

I'm going through Jastor's documentation:
There's an Objective-C implementation for returning arrays:
+ (Class)categories_class {
return [ProductCategory class];
}
This is my attempt at converting it to Swift, however it ends up not returning anything so I don't think it's implemented correctly:
#<_TtC4TestApp4Room: id = (null) {
resultCount = 50; // 50 is returning fine
results = ( // results is not
);
}>
NSDictionary response:
{
"resultCount" : 50,
"results" : [
{
"collectionExplicitness" : "notExplicit",
"discCount" : 1,
"artworkUrl60" : "http:\/\/a4.mzstatic.com\/us\/r30\/Features\/2a\/b7\/da\/dj.kkirmfzh.60x60-50.jpg",
"collectionCensoredName" : "Changes in Latitudes, Changes in Attitudes (Ultmate Master Disk Gold CD Reissue)"
}
]
}
Music.swift (not quite sure how to implement the results_class() method)
class Music : Jastor {
var resultCount: NSNumber = 0
var results: NSArray = []
class func results_class() -> AnyClass {
return Author.self
}
}
Author.swift
class Author {
var collectionExplicitness: NSString = ""
var discCount: NSNumber = 0
var artworkUrl60: NSString = ""
var collectionCensoredName: NSString = ""
}
I'm using the following syntax (adapted to your example):
static let results_class = Author.self
and everything works for me.
Other differences that may or may not have an effect:
I'm using Int instead of NSNumber and String instead of NSString (except for arrays).
I'm using implicitly wrapped optionals rather than assigning a default value to each field

Detecting a change in a compound property

Objective-C would not allow you to run the following code:
myShape.origin.x = 50
This made it easy to detect changes in the origin, since someone using your class was forced to write myShape.origin = newOrigin, and thus you could easily tie in to the setter of this property.
Swift now allows you to perform the original, formerly-disallowed code. Assuming the following class structure, how would you detect the change to the origin in order to execute your own code (e.g. to update the screen)?
struct Point {
var x = 0
var y = 0
}
class Shape {
var origin: Point = Point()
}
Update: Perhaps I should have been more explicit, but assume I don't want to modify the Point struct. The reason is that Shape is but one class that uses Point, there may very well be hundreds of others, not to mention that the origin is not the only way a Point may be used.
Property observers (willSet and didSet) do fire when sub-properties of that property are changed. In this case, when the x or y values of the Point structure change, that property will be set.
Here is my example playground code:
struct Point : Printable
{
var x = 0
var y = 0
var description : String {
{
return "(\(x), \(y))";
}
}
class Shape
{
var origin : Point = Point()
{
willSet(newOrigin)
{
println("Changing origin to \(newOrigin.description)!")
}
}
}
let circle = Shape()
circle.origin.x = 42
circle.origin.y = 77
And here is the console output:
Changing origin to (42, 0)!
Changing origin to (42, 77)!
Doesn't this work?
class Shape {
var origin: Point {
willSet(aNewValueForOrigin) {
// pre flight code
}
didSet(theOldValueOfOrigin) {
// post flight code
}
}
}
Edit: revisited code and added name of arguments to reflect what to expect.
You can use Property Observers also works for structs
Link to the part on the ebook
class StepCounter {
var totalSteps: Int = 0 {
willSet(newTotalSteps) {
println("About to set totalSteps to \(newTotalSteps)")
}
didSet {
if totalSteps > oldValue {
println("Added \(totalSteps - oldValue) steps")
}
}
}
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
// About to set totalSteps to 200
// Added 200 steps
stepCounter.totalSteps = 360
// About to set totalSteps to 360
// Added 160 steps
stepCounter.totalSteps = 896
// About to set totalSteps to 896
// Added 536 steps
Use didSet, e.g.,
struct Point {
var x = 0
var y: Int = 0 {
didSet {
println("blah blah")
}
}
}
class Shape {
var origin: Point = Point()
}
let s = Shape()
s.origin.y = 2