How to implement a selectable array struct in swift? - arrays

I would like to implement an array struct which can mark a single element is able to be as selected. Thus, that element can be easily accessed.
I am creating a wrapper class around Array struct. Is there any good way to delegate Array methods to my SelectableArray's internal array?
Or is there other better way to do it?
class SelectableArray<Element: Comparable> {
let array = Array<Element>()
private var selectedIndex: Int? = .None
var selectedElement: Element? {
guard let index = selectedIndex else {
return .None
}
return array[index]
}
func select(index: Int) {
selectedIndex = array.indices.contains(index) ? index : .None
}
func select(element: Element) {
selectedIndex = array.indexOf(element)
}
}

Just create an append method for your wrapper class and change array to a var from a let:
class SelectableArray<Element: Comparable> {
var array = Array<Element>()
private var selectedIndex: Int? = .None
// other functions...
func append(element: Element) {
self.array.append(element)
}
}

Related

swift 5: how to check array generic type?

var array: Array<Int>? = nil
var arrayType: Any.Type = type(of: array)
print("type:\(arrayType)")
I got printed:
type:Optional<Array<Int>>
then, how can i get the type Int from arrayType?
You may not understand why I did this. The actual situation is:
I have a bean, like this:
protocol Initializable {
init()
}
class MyBean1: Initializable {
required init() {}
var property1: Int?
var property2: String?
}
class MyBean2: Initializable {
required init() {}
var beans: Array<MyBean1>?
}
I have json data like this:
{beans:[{"property1":1,"property2":"string1"},{"property1":2,"property2":"string2"}]}
I want automatic create object and set the values.
I use the Runtime(A Swift Runtime library for viewing type info, and the dynamic getting and setting of properties.) Framework to mirror and set properties.
I extend the TypeInfo:
extension TypeInfo {
func properties() -> [String] {
var properties: [String] = []
for property in self.properties {
properties.append(property.name)
}
return properties
}
}
I want create object from json string:
static func fromJson<Result: Initializable>(json: String, type: Result.Type) -> Result {
var result: Initializable = Result.init()
if let dictionary = Dictionary<String, Any>.from(json: json) {
if let info: TypeInfo = try? typeInfo(of: type(of: result)) {
let properties = info.properties()
for (key, value) in dictionary {
if properties.contains(key) {
if let property: PropertyInfo = try? info.property(named: key) {
print("\(key):\(property.type)")
}
}
}
}
}
return result as! Result
}
I got print:
beans:Optional<Array<MyBean1>>
the property.type type is Any.Type.
I want get the type MyBean1 and create MyBean1's object from property.type.
Now i use Bundle.main.classNamed to get the generic type.
TypeName got from substring
var typeName = "\(property.type)" // Optional<Array<MyBean1>>
if let range = typeName.range(of: "Optional<") {
typeName = typeName.replacingCharacters(in: range, with: "")
typeName = String(typeName[typeName.startIndex..<(typeName.index(before: typeName.endIndex))])
}
print("typeName:\(typeName)") // Array<MyBean1>
if let range = typeName.range(of: "Array<") {
typeName = typeName.replacingCharacters(in: range, with: "")
typeName = String(typeName[typeName.startIndex..<(typeName.index(before: typeName.endIndex))])
print("typeName:\(typeName)") // MyBean1
if let namespace = Bundle.main.infoDictionary?["CFBundleExecutable"] as? String {
let clazz: AnyClass? = Bundle.main.classNamed("\(namespace).\(typeName)")
if let initableType = clazz as? Initializable.Type {
var beans = Array<Any>()
for item in array {
var bean = initableType.init()
// set bean properties
beans.append(bean)
}
}
}
}

#Published array in multiple ViewModels - SwiftUI

I have app with only two Views, both Views has own ViewModel. ViewA shows and manipulate objects from selectedNumbers array. ViewB owns all available objects(numbers) - in this view I want manipulate selectedNumbers array, that is used by ViewA.
I'm trying to find out, how to share these selectedNumbers array between these two ViewModels. I tried to use EnvironmentObject, StaticObject etc. But nothing works as I need. What approach should I use to achieve desired result. Thanks for help!
import SwiftUI
struct ViewA: View {
#ObservedObject var viewModel = ViewModelA()
var body: some View {
VStack {
Text("\(viewModel.number)")
.font(.largeTitle)
.padding()
.onTapGesture {
viewModel.showNext()
}
ViewB()
}
}
}
class ViewModelA: ObservableObject {
var numbers: [Int] = []
#Published var number: Int
var index = 0
init() {
number = numbers.isEmpty ? 0 : numbers[index]
}
func showNext() {
guard !numbers.isEmpty else { return }
if index < numbers.count - 1 {
index += 1
} else {
index = 0
}
number = numbers[index]
}
}
struct ViewB: View {
#ObservedObject var viewModel = ViewModelB()
var body: some View {
HStack {
ForEach(viewModel.numbers, id: \.self) { number in
Text("\(number)")
.foregroundColor(viewModel.selectedNumbers.contains(number) ? .red : .black)
.onTapGesture {
viewModel.updateSelection(number)
}
}
}
}
}
class ViewModelB: ObservableObject {
#Published var numbers: [Int] = []
#Published var selectedNumbers: [Int] = []
init() {
numbers.append(contentsOf: [1,2,3,4,5,6,7,8])
}
func updateSelection(_ number: Int) {
if selectedNumbers.contains(number) {
selectedNumbers.remove(number)
} else {
selectedNumbers.append(number)
}
}
}
extension Array where Element: Equatable {
mutating func remove(_ object: Element) {
guard let index = firstIndex(of: object) else {return}
remove(at: index)
}
}
You can still keep the logic separate, but you need to keep a single source of truth, and if you want to share data among views, you either need to pass Bindings or you can also share #ObservedObject among Subviews.
import SwiftUI
struct ViewA: View {
#ObservedObject var viewModel = ViewModelA(modelB: ViewModelB())
var body: some View {
VStack {
Text("\(viewModel.number)")
.font(.largeTitle)
.padding()
.onTapGesture {
viewModel.showNext()
}
ViewB(model: viewModel)
}
}
}
class ViewModelA: ObservableObject {
var numbers: [Int] = []
#Published var number: Int
#Published var modelB:ViewModelB
var index = 0
init(modelB:ViewModelB) {
self.modelB = modelB
number = numbers.isEmpty ? 0 : modelB.selectedNumbers[index]
}
func showNext() {
guard !modelB.selectedNumbers.isEmpty else { return }
if index < modelB.selectedNumbers.count - 1 {
index += 1
} else {
index = 0
}
number = modelB.selectedNumbers[index]
}
}
struct ViewB: View {
#ObservedObject var model : ViewModelA
var body: some View {
HStack {
ForEach(model.modelB.selectedNumbers, id: \.self) { number in
Text("\(number)")
.foregroundColor(model.modelB.selectedNumbers.contains(number) ? .red : .black)
.onTapGesture {
model.modelB.updateSelection(number)
}
}
}
}
}
struct ViewModelB {
var selectedNumbers: [Int] = []
init() {
selectedNumbers.append(contentsOf: [1,2,3,4,5,6,7,8])
}
mutating func updateSelection(_ number: Int) {
if selectedNumbers.contains(number) {
selectedNumbers.remove(number)
} else {
selectedNumbers.append(number)
}
}
}
extension Array where Element: Equatable {
mutating func remove(_ object: Element) {
guard let index = firstIndex(of: object) else {return}
remove(at: index)
}
}

Error while using index method of Array class

I have created a custom class MyArray for practice and learning. I have created a few methods inside that class and an internal array. I am facing an error while using the index(of: ) method on the internal array defined inside the class. Code of my class is as below:
public class MyArray<Element> {
private var factorialNumber: Double = 0
private var arr: [Element] = []
internal var instance: MyArray?
private init() {}
public func removeElement(_ item: Element) -> [Element] {
var found = true
while found {
if arr.contains(item) { // ERROR: Missing argument label '"where:" in call
let index = arr.index(of: item) // Error: cannot invoke index with an argument list of type (of: Element)
arr.remove(at: index!)
} else {
found = false
}
}
return arr
}
public func removeFirstOccurance(of value: Element) -> [Element] {
let index : Int = arr.index(of: value)
arr.remove(at: index!) // ERROR: Cannot force wrap value of non optional type Int
return arr
}
}

How to convert an array of custom objects to an array of strings?

I currently have an array of custom objects
[GenrePosters]
which is defined like so:
public struct GenrePosters: Decodable, Equatable{
public let poster : String
public init? (json: JSON) {
guard let poster: String = "poster_path" <~~ json
else {return nil}
self.poster = poster
}
public static func ==(lhs: GenrePosters, rhs: GenrePosters) -> Bool {
return lhs.poster == rhs.poster
}
When printed to console it looks like this:
[MyMovieGuide.GenrePosters(poster:
"/e1mjopzAS2KNsvpbpahQ1a6SkSn.jpg"), MyMovieGuide.GenrePosters(poster:
"/jjBgi2r5cRt36xF6iNUEhzscEcb.jpg"), MyMovieGuide.GenrePosters(poster:
"/tIKFBxBZhSXpIITiiB5Ws8VGXjt.jpg")]
I'm trying to convert the array of GenrePosters to an array of strings with only the poster values that like this:
[
"/e1mjopzAS2KNsvpbpahQ1a6SkSn.jpg"
"/jjBgi2r5cRt36xF6iNUEhzscEcb.jpg"
"/tIKFBxBZhSXpIITiiB5Ws8VGXjt.jpg"]
Any help will be appreciated!
You should be able to do this using map(_:) method:
let posters = posterList.map {$0.poster}
public struct GenrePosters: Decodable, Equatable{
public let poster : String
public init? (json: JSON) {
guard let poster: String = "poster_path" <~~ json
else {return nil}
self.poster = poster
}
public static func ==(lhs: GenrePosters, rhs: GenrePosters) -> Bool {
return lhs.poster == rhs.poster
}
}
let genrePostersArray = [GenrePosters(poster: "one"), GenrePosters(poster: "two"), GenrePosters(poster: "three")]
let genrePostersStringArray = genrePostersArray.flatMap { $0.poster }
print(genrePostersStringArray) // ["one", "two", "three"]

How to sort a typed array in swift?

I built a custom class which holds an "internal" array and offers some useful methods.
class ArrayList<T> {
private var array : Array<T>
public init() {
array = Array<T>()
}
public func add(element : T) {
array.append(element)
}
public func size() -> Int {
return array.count
}
...
}
Ok, that works fine for me so far.
But now I also want to have a method to sort the array. What I already have is the following:
public func sort(comparator : ?) {
array = array.sort(comparator)
}
The question mark stands for the parameter type and that is my problem: Which type must the parameter have? I read something about #noescape<,> but I can't get it to work!
I'm using Swift 2.2.
The easiest way is the use the standard closure
public func sort(comparator : (T, T) -> Bool) {
array.sortInPlace(comparator)
}
and constrain the generic type to the Comparable protocol
class ArrayList<T : Comparable>
Then you can use this code
let arrayList = ArrayList<Int>()
arrayList.add(5)
arrayList.add(12)
arrayList.add(10)
arrayList.add(2)
arrayList.sort { $0 < $1 }
print(arrayList.array) // [2, 5, 10, 12]

Resources