I'm learning swift and this seems like a simple problem yet I cannot figure it out. I'm making a trivia game to learn the basics and would like to store my question bank in a separate .swift file.
I can see the class come up in xcodes intellisense, but have no clue how to actually use the constants I have made in that file. The constants are of type dictionary and they contain the question, answers, correct answer, and question number within their key and value pairs.
In my main ViewController, I have an empty array I create and would like to populate it with the constant dictionary questions I have contained within my separate .swift file:
var BasicQuestionBank: [[String:String]] = [] // Array for multiple dictionary storage
func AddingToQuestionBankOne() {
BasicQuestionBank.append(Questions.????????) // Can't figure out how to put this in array from separate file.
}
My separate .swift file looks simply like this but has multiple questions:
public class Questions {
let question1 = [
"question": "A square has how many sides?",
"answerA": "1",
"answerB": "2",
"answerC": "3",
"answerD": "4",
"correctAnswer": "answerD",
"questionNumber": "A-001-001"]
}
Also, to populate question banks arrays programmatically by the "questionNumber" key, is this possible? Say I have 20 questions with 10 being "A-001" prefix and 10 being "A-002" prefix, how would I choose to pick the 10 out of my choice? I have tried putting these in a for-in loop, but again, cannot figure out how to access this other .swift file.
Any help and insight would be greatly appreciated.
In your example, you've defined a class, Questions, that has a constant member variable. However, you need to create an "instance" of that class in order to access its member variable:
public class Questions {
let question1 = [ etc. ]
}
var basicQuestionBank: [[String:String]] = []
func addToQuestionBankOne() {
// qs is an instance of the Questions class
let qs = Questions()
// and has a member variable, question1
basicQuestionBank.append(qs.question1)
}
Classes are like templates. You create instances of classes (these are called objects) that all have the same member variables and methods, but that can hold different values in their member variables.
In this case, it might seem a bit confusing, because you've created a class that has only one member, which is a let (so it can never hold anything other than its initial value), and no init method (which is the only place you could set question1 to be anything different than the default value you've given it). But nonetheless, you can only access that value by creating an instance of Questions, then accessing the question1 property on that instance.
By the way, for your next step you might want to try representing your questions as a struct, rather than a dictionary of strings:
struct QandA {
let question: String
let answers: [String]
let correctAnswer: Int
let questionNumber: String
}
let question1 = QandA(
question: "A square has how many sides?",
answers: ["One","Two","Three","Four"],
correctAnswer: 4,
questionNumber: "A-001-001")
Putting your data in a structure like this makes it much easier to operate on:
println(question1.question)
for (number, answer) in enumerate(question1.answers) {
println("\(number): \(answer)")
}
Just like above, this is creating an instance of the QandA struct (structs are similar to classes – the Swift book has more details on the differences), called question1, then accessing the properties of this instance.
You can enable Core Data in your project, then create a new Core Data object called "Question". Add fields for question, answers, correct answer, etc. Then, in AppDelegate, create the questions and add them to the ManagedObjectContext first time that app loads. You can find a good tutorial for Core Data in Swift here and here. If you ever want to change the questions, just change them in AppDelegate.swift and have the app reload them the next time it opens.
Related
This is a bit confusing so I apologise. But:
I am trying to make an app where the user has the ability to add items to a list of individual items that will be displayed back to them(Something along the lines of a todo list app) as a table view.
However, I have hit a roadblock I need to store several different bits of data for each item(Some Strings, Some ints and a date) in the list.
I think that a class(or struct) would be the best way to do this where an instance of the class holds the information need for each item and then the name of that instance is stored in a list so it can be accessed via the indexPath in the table view.
However, I don't know how I am going to make a new instance of the class for every item because the app could have hundreds of individual items.
I'm sorry, this is so confusing and any help would be appreciated! Feel free to ask for more info
Edit: what I am looking for and I'm sure there's a stupidly easy way of doing it but I'm try to work out how to create an instance of a class when the name of the class is stored in a variable. Ecencialy I want the instance of the class to store the item. To be created when the user inputs the item to be added to the table.
Eg. They enter an item. item1 and the other data that goes along with then I want to be able to store that in instance of the item class but I don't know how to make the name of that instance because the name I want which is item 1 is stored in a variable.
Sorry that's so confusing that's the reason I need help
So first: You can't store the Name of a Class in a Variable and use this variable to get a new instance of a class.
What you need is an Array containing all the different Items. This array is unlimited so you can store as many items in it as you like and you don't need to set a name for each of these Instances.
First create an empty array containing all Items as a property:
var items : [Item] = []
Second call the function populateItemArray() in your viewDidLoad():
private func populateItemArray() {
// Populate the items Array (Are you using CoreData, Realm, ...?)
}
Third, use the Item Array to populate the TableView.
REMEMBER: If your only using one section in your tableView, indexPath.row is always equal to the corresponding item in the array.
E.G. items[indexPath.row]
Hope this helps you!
UPDATE:
Look at this example struct Item. As you can see you can also store a date in it:
struct Item {
// First create all the properties for your struct.
var data1: String
var data2: Int
var data3: String
// You can create properties of any type you want, also of type date.
var dateOfCreation : Date
// Then implement all the methods of your custom Struct Item.
func setDate(with date : Date) {
self.dateOfCreation = date
}
func returnDate() -> Date {
return self.dateOfCreation
}
}
I'm currently writing an NSManagedObject and am looking for naming convention ideas. Here's my code:
final class StrokeSample: NSManagedObject {
#NSManaged private var location: String
var _location: CGPoint {
get {
return NSCoder.cgPoint(for: location)
}
set {
location = NSCoder.string(for: newValue)
}
}
}
As you can see, I have a CGPoint object that is stored into Core Data as a String. The issue I have here, is how should I name those two variables that describe the exact same property in an elegant way?
What I've already considered:
In my xcdatamodel object, naming the attributes string[PropertyName]. Pros: resolves the ambiguity of, for example, having a property named rect that is described as String in the data model. Also, outside the data model, everything is very clear. Cons: Writing string before every attribute looks messy in the model.
Putting a _ before the computed properties' names in my NSManagedObject. This is what is highlighted in my example. Pros: This leaves the xcdatamodel clean. Cons: forces me to use underscores everywhere in my Swift code. 🤢
Putting a _ before the attribute name in the Data Model. Xcode prevents it.
I think option 1 is the better one since the mess is restricted at only one place. But if you have better ideas on this particular issue, they are welcome. Thank you 🙂
The convention seems to be to put an underscore after a property name when you want to make it "private" as in the data model it must start with a letter.
We are a looking for a value type design pattern in swift that will allow us to create a shopping cart to hold Products. We are currently using a class but that is a reference type and when we try to add two different version of the same product (i.e. with a different colors or sizes), the first item we added gets changed to the second item we added because it points to the same object in memory.
The design pattern needs to be “global” so we can access it from any page in the app. Right now this is our Cart class that stores all the items in the cart. What do we need to do to make this a value type or how does it need to be reengineered to use a struct without a class?
class Cart : NSObject {
var allProductsInCart = [MainProduct]()
override init() {
super.init()
}
class var sharedCart: Cart {
struct Static {
static let instance = Cart()
}
return Static.instance
}
}
The problem we are getting is that we need the products in the cart to be of custom class “MainProduct.” Right now as you can see, they are stored as “MainProduct.” Do we need to switch the products to a struct or other design pattern as well? How would we do that?
Yes, given the desired behavior between a value type vs. reference type you should use a Struct.
A commonly used "pattern" for doing this is called "Redux".
The idea is that you have one, immutable version of the "state" of your app.
It can be accessed from anywhere and the only way to update it is through "actions". These will reconstruct the entire state with the required updates.
ViewControllers and views, etc... subscribe to updates of various parts of the state.
So you could have an AppState that contains a ShoppingCartState. When a product is added to it your CartViewController will be informed of this update and can update its view etc...
There are many different frameworks that are built to use Redux so I won't recommend one as you should find the one that is right for you. But I believe this pattern best suits the usage you are after.
I have two dictionaries. Both declared in a viewController, both based on a model structure class.
// ItemDictionary
var ItemDictionary = ItemModel()
var JSONDictionary = ItemModel()
JSON data is fed into the JSONDictionary and then this data is passed to ItemDictionary which feeds a table within ViewDidLoad.
self.ItemDictionary = self.JSONDictionary
All good. The table is nicely populated from JSON data. I can also delete items from the table and the ItemDictionary. However, when I try and add items back by referring to the original dictionary (JSONDictionary) the item has gone.
I understand this is expected. If Dictionary1 = Dictionary2, a new dictionary is not actually created. Only an second address. So if you change Dictionary1, Dictionary2 also changes.
A practical example could be setting a price range. You can reduce the range and have less items displayed on the table. But you can't replace previously deleted items if you wanted to increase the price range. I do not want to recall the JSON script each time I edit the range. Advice appreciated.
As confirmed by the OP, ItemModel is a class and not a Dictionary. To fix this you need to make ItemModel a real Dictionary and thus a value type. This is probably the preferred choice but will be more work.
An alternative would be to add an convenience initializer to the ItemModel class that instantiates a new copy of itself and call that instead of setting self.ItemDictionary = self.JSONDictionary.
Something like this.
init(model: ItemDictionary) -> ItemDictionary {
// copy properties
}
Then create the new copy.
self.ItemDictionary = ItemDictionary(self.JSONDictionary)
Later you can reinitialize ItemDictionary with the same call.
Try this code out-
var dictionary1 = ["name": "Magnus"]
var dictionary2 = dictionary1
dictionary2.removeAll()
print("\(dictionary2) \(dictionary1)")
The output is :-
[:] ["name": "Magnus"]
Thus 2 new dictionaries are being created. If you refer to the Swift documentation, You will find that in swift, references are hardly present.
Some other portion of code might be responsible. Hope this helps :-)
I need help with passing json data.I have json data in this array
var TableData:Array< String > = Array < String >()
In this Array I have Name,Address,Latitude, Longitude
I show Name and Address in a tableView , but I would like to create annotation in different viewController with Latitude and Longitude depending on which cell user taps(Name,Adress,latitude,Longitude shoulld be equal) , so I am asking you if there is some good tutorial in swift , or if you have just some advice.
Thank you.
There are many different ways to pass data from one swift file to another. In the case that there is a rootViewController and a button is clicked to open a new ViewController. Having previously defined an array in the new ViewController, the json parsed array can be passed along using the prepareForSegue method.
Another popular way to pass information between different swift files would be using the AppDelegate. Here you can create instances of different swift classes using a method known as instantiateViewControllerWithIdentifier("identifier"). This can be done by creating a storyboard variable then calling this method by doing storyboard.instantiateViewControllerWithIdentifier("identifier").
let newvc = self.storyboard?.instantiateViewControllerWithIdentifier("newvcIdentifier") as! (UIViewController extended class)
newvc.data = TableData
self.navigationController?.pushViewController(newController, animated: true)
where newvc has a variable declared as follows:
var data: Array <String>!
Another method that can be used is having shared data among all of the classes using a singleton. A singleton can be created very simply in swift3, take a look here for details.
class JSONData {
var json: Array <String>
static let sharedInstance = JSONData()
}
Before segueing to the next vc, you should store the data in the sharedInstance class. This should be done by overriding the prepare method. Documentation on this method can be found here
sharedInstance.json = self.json
Once the data is set in the shared instance, after the new view controller is loaded, this data can be accessed through sharedInstance.json.
Hope this works for you and just comment if you have any other questions!
I would do something like this:
let newController = self.storyboard?.instantiateViewControllerWithIdentifier("newControllerIdentifier") as! NewControllerClassName
newController.data = array
self.navigationController?.pushViewController(newController, animated: true)
It also appears you are using a array of string type that are comma separated.
I would rather create a variable like below
var jsonArray:[[String:String]]?
so I am asking you if there is some good tutorial in swift
http://matteomanferdini.com/how-ios-view-controllers-communicate-with-each-other/
He covers clean code and best practices. Better read this before implementing anything.
or if you have just some advice
Your code needs to be clean. Passing latitude as String it's going to create you a problem later (conversions, code understanding, or even bugs).
Make a new account here and watch uncle Bob. His lessons are valuable. matteomanferdini above follow uncle bob practices.
https://www.safaribooksonline.com/library/view/clean-code/9780134661742/