protobuf based spring boot end point giving no output to swift client - swift

I have a spring boot based micro-service endpoint which produces protobuf. Here is the .proto definition:
syntax = "proto3";
package TournamentFilterPackage;
import "google/protobuf/any.proto";
option java_package = "com.mycompany.service.tournament.proto";
option java_outer_classname = "TournamentCompleteData";
message TournamentData {
repeated TournamentRecord tournamentRecords = 1;
}
message TournamentRecord {
int64 id = 1;
Date start = 2;
float prize = 3;
string name = 4;
string speed = 5;
string type = 6;
float buyIn = 7;
int32 noOfParticipants = 8;
string status = 9;
}
message Date {
int32 year = 1;
int32 month = 2;
int32 day = 3;
}
message PokerResponseProto {
repeated int32 errorCodes = 1;
google.protobuf.Any responseObject = 2;
}
Here is my rest controller:
#ApiOperation(value="Complete Tournament Data", response = PokerResponseProto.class)
#GetMapping(value="/tournaments/completedata", produces = "application/x-protobuf")
public ResponseEntity<PokerResponseProto> getCompleteTournamentdata() {
if (logger.isInfoEnabled()) {
logger.info("BEGIN::/lobby/api/v1//tournaments/completedata/ " + "GET ::");
}
List<TournamentTypeResponseDto> tournamentTypeResponseDtos = new ArrayList<>();
List<TournamentRecord> tournamentRecords = new ArrayList<>();
ResponseEntity<PokerResponseProto> pokerResponse = null;
try {
tournamentTypeResponseDtos =
tournamentTypeDataService.getCompleteTournamentList();
for(TournamentTypeResponseDto t:tournamentTypeResponseDtos) {
tournamentRecords.add(controllerUtils.buildTournamentRecord(t));
}
TournamentData.Builder tournamentDataBuilder =
TournamentData.newBuilder().addAllTournamentRecords(tournamentRecords);
pokerResponse = new ResponseEntity<>(BKPokerResponseProto.newBuilder()
.setResponseObject(Any.pack(tournamentDataBuilder.build()))
.build(),
HttpStatus.OK);
logger.info("pokerResponse: {}", pokerResponse.toString());
} catch (PokerException pe) {
if (this.logger.isErrorEnabled()) {
this.logger.error(pe.getMessage(), bkpe);
}
List<Integer> errorCodeValue = controllerUtils
.convertErrorCodesToInt(bkpe.getErrorCodes());
pokerResponse = new ResponseEntity<>(PokerResponseProto.newBuilder()
.addAllErrorCodes(errorCodeValue)
.build(), HttpStatus.INTERNAL_SERVER_ERROR);
}
if (logger.isInfoEnabled()) {
logger.info("RETURN::/lobby/api/v1//tournaments/completedata/ " + "GET :: {}",
pokerResponse.toString());
}
return pokerResponse;
}
Here is the code snippet from ControllerUtils class:
public TournamentRecord buildTournamentRecord(TournamentTypeResponseDto dto) {
Instant tournamentStart = dto.getTournamentStartDate();
LocalDate localDate1 = LocalDateTime.ofInstant(tournamentStart,
ZoneOffset.UTC).toLocalDate();
Date.Builder dateBuilder1 = Date.newBuilder();
dateBuilder1.setYear(localDate1.getYear());
dateBuilder1.setMonth(localDate1.getMonthValue());
dateBuilder1.setDay(localDate1.getDayOfMonth());
TournamentCompleteData.Date trnamntStartDate = dateBuilder1.build();
TournamentRecord tr = TournamentRecord.newBuilder()
.setId(dto.getTournamentTypeId())
.setStart(trnamntStartDate)
.setPrize(dto.getFirstPrize())
.setName(dto.getTournamentName())
.setStatus(dto.getStatusName())
.build();
return tr;
}
I have written a test-case which can access this end-point & print the tournaments. But Swift client gets null when it invokes this end point. Already existing protobuf end points work fine with swift client & I have a ProtobufHttpMessageConverter already configured
#Bean
ProtobufHttpMessageConverter protobufHttpMessageConverter() {
return new ProtobufHttpMessageConverter();
}
And when the swift client invokes the end point, log messages get printed. That means client is invoking the end-point.
Here is the swift client code snippet for LobbyAPI:
import Foundation
class LobbyAPI: Api {
func getTournammentTableList(completion: #escaping ((TournamentFilterPackage_TournamentData?, Error?) -> Void)) {
getData(WithUrl: (APIConstants.lobbyURL) + "tournaments/completedata/", APIKey: APIConstants.apiKey, completion: completion)
}
For TournamentTableListVM
import Cocoa
import RealmSwift
class TournamentTableListVM: NSObject {
private weak var tblListView: LobbyTableListView!
private var tblPreviewVM: TablePreviewVM!
var arrFilterTableList: [LobbyFilterPackage_LobbyTableRecord] = []
var arrTableList: [LobbyFilterPackage_LobbyTableRecord] = []
var timer = Timer()
var didSelectTable: ((_ tableRecord: LobbyFilterPackage_LobbyTableRecord,_ isObserver:Bool) -> Void)!
var getFilterCallBack:(() -> Void)?
var currentSelectedMoneyType: (() -> TabMenuMoneyToggleView.Money) = { .play }{
didSet{
tblPreviewVM.currentSelectedMoneyType = currentSelectedMoneyType
}
}
var didTakeActionFromTablePreview: ((TablePreviewVM.TablePreviewPlayerActionType, Int) -> Void)?
//For sorting
var currentSortKey: String?
var currentSortAscending: Bool?
var aUserData: APPokerProto_PlayerProfileProto!
var observClick = false
func getCompleteTableData() {
LobbyAPI().getTournammentTableList() {[weak self] (aTournammentData, aError) in
guard let self = self else { return }
guard aError == nil, let tableData = aTournammentData, tableData.lobbyTableRecords.count > 0 else {
self.emptyTableAndPreview()
LoginManager.shared.didToggleNotify()
return
}
LoginManager.shared.didToggleNotify()
self.arrFilterTableList = tableData.lobbyTableRecords
self.arrTableList = tableData.lobbyTableRecords
self.tblListView.tblList.reloadData()
self.tblListView.tblList.selectRowIndexes(NSIndexSet.init(index: 0) as IndexSet, byExtendingSelection: true)
self.timeStamp = String(tableData.timeStamp.seconds)
}
}
In this above code "aTournammentData" is coming as nil.
Update Found that in front end swift, it was mapped to wrong generated swift struct. With the help of https://github.com/apple/swift-protobuf plugin TournamentCompleteData.pb.swift file was generated as below
// DO NOT EDIT.
// swift-format-ignore-file
//
// Generated by the Swift generator plugin for the protocol buffer compiler.
// Source: TournamentCompleteData.proto
//
// For information on using the generated types, please see the documentation:
// https://github.com/apple/swift-protobuf/
import Foundation
import SwiftProtobuf
// If the compiler emits an error on this type, it is because this file
// was generated by a version of the `protoc` Swift plug-in that is
// incompatible with the version of SwiftProtobuf to which you are linking.
// Please ensure that you are building against the same version of the API
// that was used to generate this file.
fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck {
struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {}
typealias Version = _2
}
struct TournamentFilterPackage_TournamentData {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
var tournamentRecords: [TournamentFilterPackage_TournamentRecord] = []
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
}
struct TournamentFilterPackage_TournamentRecord {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
var id: Int64 = 0
var start: TournamentFilterPackage_Date {
get {return _start ?? TournamentFilterPackage_Date()}
set {_start = newValue}
}
/// Returns true if `start` has been explicitly set.
var hasStart: Bool {return self._start != nil}
/// Clears the value of `start`. Subsequent reads from it will return its default value.
mutating func clearStart() {self._start = nil}
var prize: Float = 0
var name: String = String()
var speed: String = String()
var type: String = String()
var buyIn: Float = 0
var noOfParticipants: Int32 = 0
var status: String = String()
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
fileprivate var _start: TournamentFilterPackage_Date? = nil
}
struct TournamentFilterPackage_Date {
// SwiftProtobuf.Message conformance is added in an extension below. See the
// `Message` and `Message+*Additions` files in the SwiftProtobuf library for
// methods supported on all messages.
/// Year of the date. Must be from 1 to 9999, or 0 to specify a date without
/// a year.
var year: Int32 = 0
/// Month of a year. Must be from 1 to 12, or 0 to specify a year without a
/// month and day.
var month: Int32 = 0
/// Day of a month. Must be from 1 to 31 and valid for the year and month, or 0
/// to specify a year by itself or a year and month where the day isn't
/// significant.
var day: Int32 = 0
var unknownFields = SwiftProtobuf.UnknownStorage()
init() {}
}
// MARK: - Code below here is support for the SwiftProtobuf runtime.
fileprivate let _protobuf_package = "TournamentFilterPackage"
extension TournamentFilterPackage_TournamentData: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".TournamentData"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "tournamentRecords"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeRepeatedMessageField(value: &self.tournamentRecords) }()
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if !self.tournamentRecords.isEmpty {
try visitor.visitRepeatedMessageField(value: self.tournamentRecords, fieldNumber: 1)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: TournamentFilterPackage_TournamentData, rhs: TournamentFilterPackage_TournamentData) -> Bool {
if lhs.tournamentRecords != rhs.tournamentRecords {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension TournamentFilterPackage_TournamentRecord: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".TournamentRecord"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "id"),
2: .same(proto: "start"),
3: .same(proto: "prize"),
4: .same(proto: "name"),
5: .same(proto: "speed"),
6: .same(proto: "type"),
7: .same(proto: "buyIn"),
8: .same(proto: "noOfParticipants"),
9: .same(proto: "status"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularInt64Field(value: &self.id) }()
case 2: try { try decoder.decodeSingularMessageField(value: &self._start) }()
case 3: try { try decoder.decodeSingularFloatField(value: &self.prize) }()
case 4: try { try decoder.decodeSingularStringField(value: &self.name) }()
case 5: try { try decoder.decodeSingularStringField(value: &self.speed) }()
case 6: try { try decoder.decodeSingularStringField(value: &self.type) }()
case 7: try { try decoder.decodeSingularFloatField(value: &self.buyIn) }()
case 8: try { try decoder.decodeSingularInt32Field(value: &self.noOfParticipants) }()
case 9: try { try decoder.decodeSingularStringField(value: &self.status) }()
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every if/case branch local when no optimizations
// are enabled. https://github.com/apple/swift-protobuf/issues/1034 and
// https://github.com/apple/swift-protobuf/issues/1182
if self.id != 0 {
try visitor.visitSingularInt64Field(value: self.id, fieldNumber: 1)
}
try { if let v = self._start {
try visitor.visitSingularMessageField(value: v, fieldNumber: 2)
} }()
if self.prize != 0 {
try visitor.visitSingularFloatField(value: self.prize, fieldNumber: 3)
}
if !self.name.isEmpty {
try visitor.visitSingularStringField(value: self.name, fieldNumber: 4)
}
if !self.speed.isEmpty {
try visitor.visitSingularStringField(value: self.speed, fieldNumber: 5)
}
if !self.type.isEmpty {
try visitor.visitSingularStringField(value: self.type, fieldNumber: 6)
}
if self.buyIn != 0 {
try visitor.visitSingularFloatField(value: self.buyIn, fieldNumber: 7)
}
if self.noOfParticipants != 0 {
try visitor.visitSingularInt32Field(value: self.noOfParticipants, fieldNumber: 8)
}
if !self.status.isEmpty {
try visitor.visitSingularStringField(value: self.status, fieldNumber: 9)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: TournamentFilterPackage_TournamentRecord, rhs: TournamentFilterPackage_TournamentRecord) -> Bool {
if lhs.id != rhs.id {return false}
if lhs._start != rhs._start {return false}
if lhs.prize != rhs.prize {return false}
if lhs.name != rhs.name {return false}
if lhs.speed != rhs.speed {return false}
if lhs.type != rhs.type {return false}
if lhs.buyIn != rhs.buyIn {return false}
if lhs.noOfParticipants != rhs.noOfParticipants {return false}
if lhs.status != rhs.status {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}
extension TournamentFilterPackage_Date: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding {
static let protoMessageName: String = _protobuf_package + ".Date"
static let _protobuf_nameMap: SwiftProtobuf._NameMap = [
1: .same(proto: "year"),
2: .same(proto: "month"),
3: .same(proto: "day"),
]
mutating func decodeMessage<D: SwiftProtobuf.Decoder>(decoder: inout D) throws {
while let fieldNumber = try decoder.nextFieldNumber() {
// The use of inline closures is to circumvent an issue where the compiler
// allocates stack space for every case branch when no optimizations are
// enabled. https://github.com/apple/swift-protobuf/issues/1034
switch fieldNumber {
case 1: try { try decoder.decodeSingularInt32Field(value: &self.year) }()
case 2: try { try decoder.decodeSingularInt32Field(value: &self.month) }()
case 3: try { try decoder.decodeSingularInt32Field(value: &self.day) }()
default: break
}
}
}
func traverse<V: SwiftProtobuf.Visitor>(visitor: inout V) throws {
if self.year != 0 {
try visitor.visitSingularInt32Field(value: self.year, fieldNumber: 1)
}
if self.month != 0 {
try visitor.visitSingularInt32Field(value: self.month, fieldNumber: 2)
}
if self.day != 0 {
try visitor.visitSingularInt32Field(value: self.day, fieldNumber: 3)
}
try unknownFields.traverse(visitor: &visitor)
}
static func ==(lhs: TournamentFilterPackage_Date, rhs: TournamentFilterPackage_Date) -> Bool {
if lhs.year != rhs.year {return false}
if lhs.month != rhs.month {return false}
if lhs.day != rhs.day {return false}
if lhs.unknownFields != rhs.unknownFields {return false}
return true
}
}

Related

How can I build a recursive function in Swift to return a String?

I have a Node class defined as follows. value: T is a String
class Node<T> {
var value: T
weak var parent: Node?
var children = [Node<T>]()
init(_ value: T) {
self.value = value
}
func add(_ node: Node<T>) {
children.append(node)
node.parent = self
}
}
I'd like to build a function to return a String of the current Node's value and all Parent values. Ideally, the function would be defined in the class. For example,
currentnode.listAllValues()
would return -> "/parent2value/parent1value/currentnodevalue"
So far the following function works with a simple print(), and I've also considered using an inout parameter.
func listAllValues(node: Node<String>) {
print(node.value)
if node.parent?.value != nil {
listAllValues(node: node.parent!)
}
}
You can achieve this with an instance method which calls itself on the parent node first, if there is one:
func listAllValues() -> String {
if let p = parent {
return "\(p.listAllValues())/\(value)"
} else {
return "/\(value)"
}
}
Or as an obfuscated one-liner:
func listAllValues() -> String {
return "\(parent?.listAllValues() ?? "")/\(value)"
}
Example:
let p2 = Node("parent2value")
let p1 = Node("parent1value") ; p2.add(p1)
let n = Node("currentNodeValue") ; p1.add(n)
print(n.listAllValues())
// --> /parent2value/parent1value/currentNodeValue
Here you go:
func desc(_ s:String? = nil) -> String {
var me = String(describing:self.value)
if let prev = s {
me += "/" + prev
}
return self.parent?.desc(me) ?? me
}
Example:
let n = Node("hey")
n.add(Node("ho"))
n.children.first?.add(Node("nonny nonny no"))
let start = n.children.first!.children.first!
print(start.desc())

Storing Generic Objects in a Heterogeneous Array and retrieving object parameter as the correct type

Ahoy everyone,
I have recently been trying to implement a Node based graph system that passes data between nodes using plugs. Similar to many of the 3D applications like houdini and maya.
I have written a similar system before using Python, and wanted to try this with Swift as my first learning excersise. Boy did I jump into the deep end on this one.
I am stuck now with Swifts Arrays, as I would like to store a list of Generic plugs.
Each plug can have its own value type float, int, color, string, Vector Matrix.
I have read up about Type Erasers and opaque types, but still cant seem to get my values our of a list in a way that I can perform some arithmetic on them.
All and any help that might put me in the direction would be greatly appreciated :D
import Foundation
import MetalKit
protocol genericPlug {
associatedtype T
func GetValue() -> T
}
class Plug<T>:genericPlug{
var _value:T?
var value:T {
get{GetValue()}
set(val){
value = val
}
}
func GetValue() -> T{
return _value!
}
init(_ newValue:T){
_value=newValue
}
}
class Node{
var plugs:[genericPlug] = []
init(){
var p1 = Plug<Int>(0)
var p2 = Plug(vector2(1.2, 3.1))
var p3 = Plug([0.0, 3.1, 0.6, 1])
plugs.append(p1)
plugs.append(p2)
plugs.append(p3)
}
func execute(){
// will access the plugs in the array and perform some sort of calculations on them.
plugs[0].value + 1 // should equal 1
plugs[1].value.x + 0.8 // should have x=2.0 y=3.1
plugs[2].value[1] - 0.1 // should equal 3.0
}
}
Thanks everyone
Use a generic something to extract what you need. Your options are methods and subscripts.
protocol PlugValue {
init()
}
extension Int: PlugValue { }
extension Float: PlugValue { }
extension Double: PlugValue { }
extension SIMD3: PlugValue where Scalar == Int32 { }
struct Plug<Value: PlugValue> {
var value: Value
init(_ value: Value) {
self.value = value
}
}
protocol AnyPlug {
var anyValue: PlugValue { get }
}
extension AnyPlug {
subscript<Value: PlugValue>(type: Value.Type = Value.self) -> Value {
anyValue as? Value ?? .init()
}
func callAsFunction<Value: PlugValue>(_ type: Value.Type = Value.self) -> Value {
anyValue as? Value ?? .init()
}
}
extension Plug: AnyPlug {
var anyValue: PlugValue { value }
}
let plugs: [AnyPlug] = [
Plug(1),
Plug(2.3 as Float),
Plug(4.5),
Plug([6, 7, 8] as SIMD3)
]
plugs[0][Int.self] // 1
plugs[1][Double.self] // 0
plugs[1][] as Float // 2.3
let double: Double = plugs[2]() // 4.5
plugs[3](SIMD3.self).y // 7
With the array of protocols, do you have to down cast them into their Plug when retrieving them every time?
Essentially. This is true of all heterogenous sequences. Here are your options:
extension Array: PlugValue where Element: PlugValue { }
let plug: AnyPlug = Plug([0.1, 1.1, 2.1])
(plug as? Plug<[Double]>)?.value[1]
(plug.anyValue as? [Double])?[1]
extension Plug {
enum Error: Swift.Error {
case typeMismatch
}
}
extension AnyPlug {
func callAsFunction<Value: PlugValue, Return>(_ closure: (Value) -> Return) throws {
guard let value = anyValue as? Value
else { throw Plug<Value>.Error.typeMismatch }
closure(value)
}
}
try plug { (doubles: [Double]) in doubles[1] } // 1.1
try plug { ($0 as [Double])[1] } // 1.1
try plug { $0 as Int } // <Swift.Int>.Error.typeMismatch
I managed to find a solution that worked for my needs.
I am still looking at finding a better way to handle getting the data and their correct type.
import Foundation
import MetalKit
// Creating the PlugType Enum
enum PlugType{
case Integer(Int?)
case Float_(Float?)
case Double_(Double?)
case Vector3(simd_int3)
// default types
static func IntegerType() -> PlugType{ return PlugType.Integer(nil)}
static func FloatType() -> PlugType{ return PlugType.Float_(nil)}
static func DoubleType() -> PlugType{ return PlugType.Double_(nil)}
}
// Implements a way to retrieve the correct value type
extension PlugType{
var IntegerValue: Int{
switch self{
case .Integer(let value):
return value ?? 0
default:
return 0
}
}
var FloatValue: Float{
switch self
{
case .Float_(let value):
return value ?? 0
default:
return 0
}
}
var DoubleValue: Double{
switch self
{
case .Double_(let value):
return value ?? 0
default:
return 0
}
}
}
// Get the string representation of the PlugType
extension PlugType {
var typeName: String{
switch self {
case .Integer: return "Integer"
case .Float_: return "Float"
case .Double_: return "Double"
case .Vector3: return "Vector3"
}
}
var swiftType: Any.Type {
switch self {
case .Integer: return Int.self
case .Float_: return Float.self
case .Double_: return Double.self
case .Vector3: return simd_int3.self
}
}
}
class Plug{
var _value:PlugType?
var type:String? { get{ return _value?.typeName } }
init(_ newValue:PlugType){
_value = newValue
}
func geee<T>(_ input:T) -> T{
switch type {
case "Integer":
return getVal(_value!.IntegerValue) as! T
case "Double":
return getVal(_value!.DoubleValue) as! T
default:
return 0 as! T
}
}
func getVal(_ val:Int) -> Int {
return val
}
func getVal(_ val:Float) -> Float {
return val
}
func getVal(_ val:Double) -> Double {
return val
}
}
var plugs:[Plug] = []
var p1 = Plug(PlugType.Integer(2))

Set does not work despite copying from Developer Documentation

Whilst attempting to create a set, I get the error stating "Cannot convert value of type '[Int]' to specified type 'Set'". This occurs even though I write the code exactly as in the DD: https://developer.apple.com/documentation/swift/set
var intSet2 : Set = [2, 3, 5, 7]
// Cannot convert value of type '[Int]' to specified type 'Set'
I have now switched to 'NSSet' (which I found by accidence) and now it appears to work. How come using normal 'Set' does not work? When I go to the DD of NSSet it says: "An object representing a static, unordered, uniquing collection, for use instead of a Set constant in cases that require reference semantics." Is this about reference types? The DD also states that arrays are value types. I'm at a loss as to when to use Set or NSSet (or even NSMutableSet for that manner).
var intSet2 : NSSet = [2, 3, 5, 7]
// Works
Lastly, when I try to convert an array of colors with type [CardColor] to a Set or NSset, I receive error message stating: "Cannot convert value of type '[CardColor]' to specified type 'NSSet'.
let colorCheck = selectedCards.map { $0.color }
var colorCheckSet : NSSet = colorCheck
// Cannot convert value of type '[CardColor]' to specified type 'NSSet'
Thanks for your help in advance.
Set model:
import Foundation
class Set {
// MARK: properties
var deck = [Card]()
var tableCards = [Card]()
var matchedCards = [Card]()
var selectedCards: [Card] {
get {
var cards = [Card]()
for card in tableCards.indices {
if tableCards[card].isSelected == true {
cards.append(tableCards[card])
}
}
return cards
}
}
var unmatchedCards = 12
var score = 0
// MARK: functions
// Selects the card. If this is the third card to be selected, proceeds to check for matches
func selectCard(at index: Int) {
if tableCards[index].isSelected == false {
if selectedCards.count < 3 {
tableCards[index].isSelected = true
if selectedCards.count == 3 {
checkIfCardsMatch()
}
}
}
else if tableCards[index].isSelected == true {
tableCards[index].isSelected = false
}
}
func checkIfCardsMatch() {
let colorCheck = selectedCards.map { $0.color }
var colorCheckSet : NSSet = colorCheck
// Cannot convert value of type '[CardColor]' to specified type 'NSSet'
var intSet2 : Set = [2, 3, 5, 7]
// Cannot convert value of type '[Int]' to specified type 'Set'
// for item in colorCheck {
// colorCheckSet.insert()
// }
// let symbolCheck: Set = selectedCards.map() { $0.symbol }
// let numberCheck: Set = selectedCards.map() { $0.number }
// let shadingCheck: Set = selectedCards.map() { $0.shading }
}
// MARK: functions
func dealThreeMoreCards() {
if unmatchedCards <= 21 {
unmatchedCards += 3
}
print(unmatchedCards)
}
//MARK: initialization
init() {
for cardcolor in CardColor.allCases {
for cardsymbol in CardSymbol.allCases {
for cardnumber in CardNumber.allCases {
for cardshading in CardShading.allCases {
let card = Card(initcolor: cardcolor, initsymbol: cardsymbol, initnumber: cardnumber, initshading: cardshading)
deck.append(card)
}
}
}
}
}
}
Card model:
import Foundation
struct Card {
var identifier: Int = 0
var isSelected = false
var color: CardColor
var symbol: CardSymbol
var number: CardNumber
var shading: CardShading
static var identifierFactory = 0
init(initcolor: CardColor, initsymbol: CardSymbol, initnumber: CardNumber, initshading: CardShading){
color = initcolor
symbol = initsymbol
number = initnumber
shading = initshading
Card.identifierFactory += 1
self.identifier = Card.identifierFactory
}
}
enum CardColor: Int, CaseIterable {
case red = 1
case green = 2
case purple = 3
// static let all = [cardColor.red, cardColor.green, cardColor.purple]
}
enum CardSymbol: Int, CaseIterable {
case ovals = 1
case squiggles = 2
case diamonds = 3
// static let all = [cardSymbol.ovals, cardSymbol.squiggles, cardSymbol.diamonds]
}
enum CardNumber: Int, CaseIterable {
case one = 1
case two = 2
case three = 3
// static let all = [cardNumber.one, cardNumber.two, cardNumber.three]
}
enum CardShading: Int, CaseIterable {
case solid = 1
case open = 2
case striped = 3
// static let all = [cardShading.solid, cardShading.open, cardShading.striped]
}
// Not every Card variable has been included below. Could cause issues later.
extension Card: Hashable {
static func == (lhs: Card, rhs: Card) -> Bool {
return lhs.identifier == rhs.identifier &&
lhs.isSelected == rhs.isSelected &&
lhs.color == rhs.color &&
lhs.symbol == rhs.symbol &&
lhs.number == rhs.number &&
lhs.shading == rhs.shading
}
func hash(into hasher: inout Hasher) {
hasher.combine(identifier)
hasher.combine(isSelected)
hasher.combine(color)
hasher.combine(symbol)
hasher.combine(number)
hasher.combine(shading)
}
}

Swift/XCode build error: Cannot convert value of type [Die] to expected argument type [Die]

Using XCode 8.3.3 which I believe uses Swift 3.1, but not 100% sure (how can you tell?). Here is the complete code. Note that when I paste this into a clean Playground, I get no errors. But within an XCode project, I get the build error on the line "let dice = Dice(withArrayOfDie: arrayOfDie)" in the unit test:
let defaultFaceCount = 6
let defaultDieCount = 6
func randomInt(withMaxValue maxValue: Int) -> Int {
return Int(arc4random_uniform(UInt32(maxValue))) + 1
}
class Die
{
private let m_faceCount: Int // Constant only set in init
private var m_faceValue: Int?
init(numFaces initialFaceCount: Int, withValue initialFaceValue: Int) {
// Make sure number of faces is greater than 0.
m_faceCount = (initialFaceCount > 0) ? initialFaceCount : defaultFaceCount
// Make sure face value is in proper range.
if initialFaceValue == 0 || initialFaceValue > m_faceCount {
m_faceValue = randomInt(withMaxValue: m_faceCount)
}
else {
m_faceValue = abs(initialFaceValue)
}
}
convenience init(numFaces initialFaceCount: Int) {
self.init(numFaces: initialFaceCount,
withValue: randomInt(withMaxValue: initialFaceCount))
}
convenience init() {
self.init(numFaces: defaultFaceCount)
}
var faceValue: Int {
get {
return m_faceValue!
}
}
var faceCount: Int {
get {
return m_faceCount
}
}
func roll() {
// face values are 1 based!
m_faceValue = randomInt(withMaxValue: m_faceCount)
}
}
class Dice {
var m_dice: [Die]
var m_occurrencesOf: [Int]
// Init with a pre-initialized array of Die. Every Die in
// the array must have the same face count.
init(withArrayOfDie: [Die]) {
var faceCount = defaultFaceCount
if withArrayOfDie.isEmpty {
// If there are no dice, add defaults.
m_dice = [Die]()
for _ in 1...defaultDieCount {
m_dice.append(Die(numFaces: defaultFaceCount))
}
}
else {
m_dice = withArrayOfDie
faceCount = m_dice[0].faceCount
}
// Keep trace of # of occurrences of each face value.
m_occurrencesOf = Array(repeating: 0, count: faceCount)
for die in m_dice {
m_occurrencesOf[die.faceValue - 1] += 1
}
}
// Init numDice dice, each with numFaces.
convenience init(numDice count: Int, numFaces faceCount: Int) {
var dice = [Die]()
for _ in 1...count {
dice.append(Die(numFaces: faceCount))
}
self.init(withArrayOfDie: dice)
}
// Init defaultDieCount dice, each with defaultFaceCount faces.
convenience init() {
self.init(numDice: defaultDieCount, numFaces: defaultFaceCount)
}
var count: Int {
return m_dice.count
}
// Retrieve the die at the specified (0 based) index.
func die(atIndex index: Int) -> Die? {
if !m_dice.isEmpty && index >= 0 && index < m_dice.count {
return m_dice[index]
}
return nil
}
subscript(index: Int) -> Die? {
get {
return die(atIndex: index)
}
}
}
// Unit Test
var arrayOfDie = [Die]()
for i in 1...6 {
arrayOfDie.append(Die())
}
let dice = Dice(withArrayOfDie: arrayOfDie)
// XCTAssertEqual(6, dice.count)
I get the build error "Cannot convert value of type [Die] to expected argument type [Die]" on the line "let dice = Dice(withArrayOfDie: arrayOfDie)". Cannot figure out why the argument which is an array of die does not match the expected init argument type.
Thanks!
Get rid of () in var m_dice: [Die]()
class Dice {
var m_dice: [Die]
init(withArrayOfDie dice: [Die]) {
m_dice = dice
}
}

How to remove duplicate characters from a string in Swift

ruby has the function string.squeeze, but I can't seem to find a swift equivalent.
For example I want to turn bookkeeper -> bokepr
Is my only option to create a set of the characters and then pull the characters from the set back to a string?
Is there a better way to do this?
Edit/update: Swift 4.2 or later
You can use a set to filter your duplicated characters:
let str = "bookkeeper"
var set = Set<Character>()
let squeezed = str.filter{ set.insert($0).inserted }
print(squeezed) // "bokepr"
Or as an extension on RangeReplaceableCollection which will also extend String and Substrings as well:
extension RangeReplaceableCollection where Element: Hashable {
var squeezed: Self {
var set = Set<Element>()
return filter{ set.insert($0).inserted }
}
}
let str = "bookkeeper"
print(str.squeezed) // "bokepr"
print(str[...].squeezed) // "bokepr"
I would use this piece of code from another answer of mine, which removes all duplicates of a sequence (keeping only the first occurrence of each), while maintaining order.
extension Sequence where Iterator.Element: Hashable {
func unique() -> [Iterator.Element] {
var alreadyAdded = Set<Iterator.Element>()
return self.filter { alreadyAdded.insert($0).inserted }
}
}
I would then wrap it with some logic which turns a String into a sequence (by getting its characters), unqiue's it, and then restores that result back into a string:
extension String {
func uniqueCharacters() -> String {
return String(self.characters.unique())
}
}
print("bookkeeper".uniqueCharacters()) // => "bokepr"
Here is a solution I found online, however I don't think it is optimal.
func removeDuplicateLetters(_ s: String) -> String {
if s.characters.count == 0 {
return ""
}
let aNum = Int("a".unicodeScalars.filter{$0.isASCII}.map{$0.value}.first!)
let characters = Array(s.lowercased().characters)
var counts = [Int](repeatElement(0, count: 26))
var visited = [Bool](repeatElement(false, count: 26))
var stack = [Character]()
var i = 0
for character in characters {
if let num = asciiValueOfCharacter(character) {
counts[num - aNum] += 1
}
}
for character in characters {
if let num = asciiValueOfCharacter(character) {
i = num - aNum
counts[i] -= 1
if visited[i] {
continue
}
while !stack.isEmpty, let peekNum = asciiValueOfCharacter(stack.last!), num < peekNum && counts[peekNum - aNum] != 0 {
visited[peekNum - aNum] = false
stack.removeLast()
}
stack.append(character)
visited[i] = true
}
}
return String(stack)
}
func asciiValueOfCharacter(_ character: Character) -> Int? {
let value = String(character).unicodeScalars.filter{$0.isASCII}.first?.value ?? 0
return Int(value)
}
Here is one way to do this using reduce(),
let newChar = str.characters.reduce("") { partial, char in
guard let _ = partial.range(of: String(char)) else {
return partial.appending(String(char))
}
return partial
}
As suggested by Leo, here is a bit shorter version of the same approach,
let newChar = str.characters.reduce("") { $0.range(of: String($1)) == nil ? $0.appending(String($1)) : $0 }
Just Another solution
let str = "Bookeeper"
let newChar = str.reduce("" , {
if $0.contains($1) {
return "\($0)"
} else {
return "\($0)\($1)"
}
})
print(str.replacingOccurrences(of: " ", with: ""))
Use filter and contains to remove duplicate values
let str = "bookkeeper"
let result = str.filter{!result.contains($0)}
print(result) //bokepr