How to make my Struct variable public? - ios

My question looks very simple. But i am struggling it for a day.
I need to declare my struct variable as a public variable. So that the UICollectionViewController Data methods can access the Struct Variable.
How can i do it ?
I tried it by myself. But i can't achieve it
class SkillsController: UICollectionViewController {
var mcnameArray :[String] = []
var mcidArray :[String] = []
func getSkills(){
Alamofire.request(.GET, "http://www.wols.com/index.php/capp/main_category_list")
.responseJSON { (_, _, data, _) in
let json = JSON(data!)
let Count = json.count
for index in 0...Count-1 {
var ds = json[index]["DISPLAY_STATUS"].string
if ds == "Y" {
var mcname = json[index]["MAIN_CATEGORY_NAME"].string
self.mcnameArray.append(mcname!)
var mcid = json[index]["MAIN_CATEGORY_ID"].string
self.mcidArray.append(mcid!)
}
}
var skill = Skills(mcname: self.mcnameArray, mcid: self.mcidArray)
println(skill.mcname.count)
}
}
override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
//#warning Incomplete method implementation -- Return the number of items in the section
return skill.mcname.count// Throws an Error
}

Your variable is not "private" but rather it's locally scoped to the function where it's defined.
If you move it next to var mcnameArray and var mcidArray, then it will be accessible from the other functions too.
However, since you are making an asynchronous network request, the variable will need to change. You might want to make it optional (var skill: Skill?) so that it can be nil at the beginning, and change later.

Related

Return a single String from a dictionary using uniqueKeysWithValues

Goal of the code:
To assign a struct dictionary with Strings as Keys and String Arrays as values to a variable and then pull one (can be at random) specific String key value in the String Array and return that one String element in the underlying String Array so that it can be used elsewhere (potentially assigned to a label.text)
Essentially (please reference code below), I want to access one value at random in myDictionary using a specific key ("keyOne"), and pull, let's say, "Value2" then return only the string "Value2" from the underlying String Array associated with "keyOne" using indexing.
Errors are in the code below.
The issue I'm thinking is that I haven't figured out how to turn my final var Testing = dict["keyOne"] into an Int compatible index... if it was an index, the code would pull an Int value and the corresponding String from the three Strings in the underlying value array (due to the three String values associated with "keyOne").
Also, variableView() just inherits the datasource from several other containers, but the var dataSource : Structure? is the main reference, so that is what I included.
Code so far:
let myDictionary = [Structure(name: "keyOne", text: ["Value1", "Value2", "Value3"]), Structure(name: "keyTwo", text: ["Value4", "Value5", "Value6"])]
lazy var dict = Dictionary(uniqueKeysWithValues: myDictionary.lazy.map { ($0.name, $0.text) })
struct Structure: Hashable {
var name: String
var text: [String]
init(name: String, text: [String]){
self.name = name
self.text = text
}
}
func variable(at index: Int) -> variableView {
let variable = variableView()
var Testing = dict["keyOne"]
variable.dataSource = Testing![index] <- Cannot assign value of type 'String' to type 'structure'
return variable
var dataSource : Structure? {
didSet {
label.text = "This is a test"
} else {
// n/a
}
}
Please note that the error message is above in the code for variable.dataSource = Testing![index].
I am also suspecting that my issue lies in the "looping" logic of how I am assigning a variable with a struct, to a datasource which references that same struct.
Any help is appreciated as I have been stuck on this for legitimately a week (I truly have exhausted every single StackOverflow answer/question pair I could find).
THANK YOU!
EDIT:
I found this documentation to assist me greatly with this, and I recommend anyone with a similar question as mine to reference this: https://swift.org/blog/dictionary-and-set-improvements/
Given the question and the discussion in the comments I would add a mutating func to the struct that removes and returns a random string
mutating func pullText() -> String? {
guard let index = text.indices.randomElement() else {
return nil
}
return text.remove(at: index)
}
Example
if let index = myDictionary.firstIndex(where: { $0.name == "keyOne" }),
let text = myDictionary[index].pullText() {
someLabel.text = text
}
Here is another example based on the code in the question
Assuming VariableView looks something like this
struct VariableView: View {
var dataSource : Structure?
var word: String?
var body: some View {
Text(word ?? "")
}
}
Then the func variable can be changed to
func variable() -> VariableView {
var variable = VariableView()
if let index = dict.firstIndex(where: { $0.name == "keyOne" }) {
variable.dataSource = dict[index]
variable.word = dict[index].pullText()
}
return variable
}

cannot able to make struct variable as mutable type

I am facing the issue of "Cannot assign to immutable expression of type 'Bool'" . Please look at the below code. I am getting error in viewForHeaderInSection. Actually where should i do modification to make it work?.
struct VenueDetail {
var isVeg: Bool
}
struct VenueDetailDTOMapper {
static func map(_ dto: DetailDataDTO) -> VenueDetail {
return VenueDetail(isVeg: dto.isVeg)
}
}
In API Manager I have get the data from api and use above struct as follow
let venueDetail = VenueDetailDTOMapper.map(getDetail)
ViewModel:
enum VenueDetailVMTypes {
case veueInfoInfo
}
protocol VenueDetailVMItems {
var type: VenueDetailVMTypes { get }
}
struct VenueInfoViewModel: VenueDetailVMItems {
var type: VenueDetailVMTypes {
return .veueInfoInfo
}
var headerSection: VenueDetail
}
func cretaDataSource() {
if let getVenueDetails = self.venueDetails {
let vmType = VenueInfoViewModel(headerSection: getVenueDetails)
arrayDataSource.append(vmType)
}
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let headerView = UIView()
let venueDetailVMItems = viewModel.arrayDataSource[section]
switch venueDetailVMItems.type {
case .veueInfoInfo:
let headerCell = tableView.dequeueReusableCell(withIdentifier: kCellIdentifierVenueHeader) as! VenueHeaderTVCell
headerCell.updateCellData(detail: (venueDetailVMItems as! VenueInfoViewModel).headerSection)
headerCell.foodTypeHandler = { [weak self] (isOn) in
guard let strongSelf = self else {
return
}
strongSelf.viewModel.showOnlyVegMenu(shouldShowVeg: isOn)
(venueDetailVMItems as! VenueInfoViewModel).headerSection.isVeg = isOn. //Cannot assign to immutable expression of type 'Bool'
strongSelf.tableView.reloadData()
}
headerView.addSubview(headerCell)
break
}
return headerView
}
Structs are value types, so each time you assign a struct, it makes a copy. You're treating it as a reference type. Stripping away all the as! casting, what you've done is:
let value = array[index]
value.someBool = true
reloadData()
Even if value were mutable (which it could be), that wouldn't do anything. value is a copy of array[index], not a reference to it. If you want it to be a reference, then you need to make it a reference type (a class).
You've used a protocol and a "type" identifier, where what I think you really wanted was an enum with associated data:
enum VenueDetail {
case veueInfoInfo(VenueInfoViewModel)
}
With this, you get rid of all of the dangerous and complicated as! casting.
But all of that doesn't really change the issue you're describing. Either way (with a protocol or with an enum), what you need to do is:
var value = array[index]
// change value the ways you want; set the bool, etc.
array[index] = value
A structure is an aggregation of fields; if a particular structure instance is mutable, its fields will be mutable; if an instance is immutable, its fields will be immutable. A structure type must thus be prepared for the possibility that the fields of any particular instance may be mutable or immutable.
Please check this
So try to change let to be var
Make sure the the arrayDataSource is mutable user var not let
var arrayDataSource = [VenueInfoViewModel]()
After struggling i just create a method in viewModel that removes objects in array of type .venueInfo and reload, i know its kind of hack but time being i have no option. In case if somebody found better way, really appreciated
func changeHeaderSwitch(isVeg: Bool) {
arrayDataSource.removeAll { (venueDetailVMItems) -> Bool in
return venueDetailVMItems.type == .veueInfoInfo
}
if var getVenueDetails = self.venueDetails {
getVenueDetails.isVeg = isVeg
let vmType = VenueInfoViewModel(headerSection: getVenueDetails, arrayMenuInfo: [])
arrayDataSource.append(vmType)
}
}

Custom collection in swift: is it a right way?

I am learning swift. I would like to use a custom class to be loopable [able to a for...in loop] like Array. Below is the given sample code that so far, I have tried. The class in question is "GuestManager" which is holding a private collection of guests [objects of class Guest]
import Foundation
class Guest{
var guestId: String
var guestName: String
init(gId: String, name: String){
self.guestId = gId
self.guestName = name
}
}
class GuestManager: GeneratorType, SequenceType{
private var guests = [Guest]?()
private var nextIndex: Int
init(guests: [Guest]){
self.guests = guests
self.nextIndex = 0
}
func next() -> Guest? {
if self.nextIndex > (self.guests?.count)! - 1 {
self.nextIndex = 0
return nil
}
let currentGuest = self.guests![self.nextIndex]
self.nextIndex += 1
return currentGuest
}
subscript(aGuestId gID: String) -> Guest{
return (self.guests?.filter({$0.guestId == gID}).first)!
}
}
I do not want to create separate classes that are conforming to GeneratorType & SequenceType protocols. Instead I have created a single class that is conforming to both protocols.
Below are some of my questions:
I would like to know if this a correct way to have a custom collection type ?
Can we use subscript as a way to perform a search based on a property for example "subscript(aGuestId gID: String)" in the sample code above ?
It is clear from the code for next() function implementation in above sample code that is resetting the "nextIndex" when the iteration reached at the end. How one will handle the situation wherein we use a break statement inside the for...in loop as below:
for aGuest in guestManager{//guestManager an instance of GuestManager class instantiated with several guest objects
print(aGuest.guestName)
}
for aG in guestManager{
print(aG.guestId)
break
}
In the 2nd for loop the code break out after getting the first Element [Guest object in this case]. The subsequent for loop will start at index 1 in the collection and not at 0. Is there anyway to handle this break situation so that for each subsequent for looping the index is always set to 0?
Thanks
Edit: It seems the "nextIndex" reset issue can be fixed with below code [added inside GuestManager class] for generate() method implementation
func generate() -> Self {
self.nextIndex = 0
return self
}
You should not store the nextIndex inside the class. You can use a local variable in the generate method and then let that variable be captured by the closure you pass to the generator you create in that method. That’s all you need to adopt SequenceType:
class GuestManager: SequenceType{
private var guests: [Guest]
init(guests: [Guest]) {
self.guests = guests
}
func generate() -> AnyGenerator<Guest> {
var nextIndex = 0
return AnyGenerator {
guard nextIndex < self.guests.endIndex else {
return nil
}
let next = self.guests[nextIndex]
nextIndex += 1
return next
}
}
}
For subscripting, you should adopt Indexable. Actually, the easiest way to fulfill all your requirements is to pass as much of the logic for SequenceType, Indexable, and eventually (if you want to support it) CollectionType, to your array, which already has these capabilities. I would write it like this:
class GuestManager {
private var guests: [Guest]
init(guests: [Guest]){
self.guests = guests
}
}
extension GuestManager: SequenceType {
typealias Generator = IndexingGenerator<GuestManager>
func generate() -> Generator {
return IndexingGenerator(self)
}
}
extension GuestManager: Indexable {
var startIndex: Int {
return guests.startIndex
}
var endIndex: Int {
return guests.endIndex
}
subscript(position: Int) -> Guest {
return guests[position]
}
}
Some more observations:
Your guests property should not be an optional. It makes the code more complicated, with no benefits. I changed it accordingly in my code.
Your Guest class should probably be a value type (a struct). GuestManager is also a good candidate for a struct unless you require the reference semantics of a class (all collection types in the standard library are structs).
I think the subscripting approach you're trying here is kind of convoluted. Personally, I would use a function to do this for the sake of clarity.
guestManager[aGuestId: guestId]
guestManager.guestWithID(guestId)
So stylistically I would probably land on something like this
import Foundation
class Guest{
var guestId: String
var guestName: String
init(guestId: String, guestName: String){
self.guestId = guestId
self.guestName = guestName
}
}
class GuestManager: GeneratorType, SequenceType{
private var guests: [Guest]
private var nextIndex = 0
init(guests: [Guest]){
self.guests = guests
}
func next() -> Guest? {
guard guests.count < nextIndex else {
nextIndex = 0
return nil
}
let currentGuest = guests[nextIndex]
nextIndex += 1
return currentGuest
}
func guestWithID(id: String) -> Guest? {
return guests.filter{$0.guestId == id}.first ?? nil
}
}

Assigning 'class variable' to variable inside 'lazy var'

I am trying to understand the logic of draggable collection view cells. It works with dummy data however, I couldn't figure out how to make it work with real data.
I couldn't know what title to give to the question, please feel free to edit/improve it
If I use this approach with a dummy array items and call the function
class TableViewController: UITableViewController, KDRearrangeableCollectionViewDelegate, UICollectionViewDataSource {
lazy var data : [[String]] = {
var array = [[String]]()
let images = [
"1.jpg", "2.jpg", "3.jpg"
]
if array.count == 0 {
var index = 0
var section = 0
for image in images {
if array.count <= section {
array.append([String]())
}
array[section].append(image)
index += 1
}
}
return array
}()
func moveDataItem(fromIndexPath : NSIndexPath, toIndexPath: NSIndexPath) {
let name = self.data[fromIndexPath.section][fromIndexPath.item]
self.data[fromIndexPath.section].removeAtIndex(fromIndexPath.item)
self.data[toIndexPath.section].insert(name, atIndex: toIndexPath.item)
print(self.data)
}
At this point print(self.data) prints the new order of the array items after dragging/rearranging.
Example print(self.data) log:
[["2.jpg", "1.jpg", "3.jpg"]]
Now I have my real data as and it gets appended by items after fetching from database..
var realImages = [NSURL]()
// I tried assigning `images` array inside `lazy var data` but received error:
lazy var data : [[String]] = {
var array = [[String]]()
let images = realImages // error here
...
Instance member realImages cannot be used on type TableViewController
What is the proper way of using my real array in that case?
(This explanation and its Git repo was great for understanding it). I simplified it even further with dummy data but I couldn't understand the logic of 'lazy var' and regular 'var' and how to make it work in this scenario
You need to use self explicitly like let images = self.realImages.
lazy var data : [[String]] = { [unonwed self] in
var array = [[String]]()
let images = self.realImages // error gone :)
(Note that we had to say [unowned self] in here to prevent a strong reference cycle)

Set class or struct type as parameter

Currently, I'm working with stream data. This is my code ported from Obj-C:
func readInt8() -> Int {
var temp = Int8()
//get value for temp
return Int(temp)
}
func readInt64() -> Int {
var temp = Int()
//get value for temp
return temp
}
func readFloat32() -> Float {
var temp = Float32()
//get value for temp
return temp
}
Here's my attempt:
func read<T: SignedIntegerType>(type: T.Type) -> Int {
//error: Missing argument for parameter 'integerLiteral' in call
var temp = T()
//getting value for temp requires it to be initialized
}
How can I make these functions generic ? Thank you.
Based on what you've done so far:
func readInt<T: SignedIntegerType>() -> T {
return T(0)
}
func readFloat<T: FloatingPointType>() -> T {
return T(0)
}
var a: Double = readFloat() //0.0
var b: Int32 = readInt() //0
could be good. Int(), Int8(), Float() do the same as they creates a new variable, and initializes to zero. So if you write T(0) explicitly, it would do the same.

Resources