I have one swift file and in which I have kept my common function that I need every time so when I am accessing that function I am not getting value
Myfirst class
import Foundation
import UIKit
class test1
{
var mydata = NSMutableArray()
//MY COMMON FUNCTION
func loadMoredata(create_at:String)->NSMutableArray
{
**//////My CODE**
//getting correct data
print(mydata)
return mydata
}
}
Mysecond Class
override func viewWillAppear(_ animated: Bool)
{
//Calling that function
// not getting data here or empty array
let dt:NSMutableArray = test1().loadMoredata(create_at: "0")
// not getting data here or empty array
print(test1().mydata)
}
You dont get anything back from your function since your mydata property is an instance property. Hence, every test1 object you create, will have its own mydata property with its own data.
If you want store global state you could make test1 a singleton class, where mydata is globally accessible.
class test1
{
static let shared = test1()
var mydata = NSMutableArray()
private init(){}
//MY COMMON FUNCTION
func loadMoredata(create_at:String)->NSMutableArray
{
**//////My CODE**
//getting correct data
print(mydata)
return mydata
}
}
Related
I have a UIViewController containing a UITableView that is populated via an array of custom class objects. These custom class objects have an array property. As you can see from my code below, the objects are equatable. When I segue to a second vC, the array of custom class objects (and obviously the array properties associated with each object) is passed over.
I have a function in the second vC that matches an object with one that is contained in the array. After matching, the property array of the object in the array that matched is updated. However, when I print what should be the updated property array, no change has been made. Below is a representation of my code:
class Object: Equatable {
var propertyArray: [String] = []
static func ==(lhs: object, rhs: object) -> Bool {
return lhs.someProperty == rhs.someProperty
}
}
class ArrayOfObjects {
var array: [Object] = []
}
class vC1: UIViewController, UITableViewDelegate, UITableViewDataSource {
var objectArray1 = ArrayOfObjects()
override viewDidLoad() {
//populate objectArray1.array
}
prepareForSegue(segue: UIStoryboardSegue) {
segue.destination.objectArray2 = objectArray1 //segue to vC2
}
}
class vC2: UIViewController {
var objectArray2 = ArrayOfObjects()
var someOtherObject = Object() //object that has same properties as an object in objectArray2
func someFunc() {
let indexOfMatchingObject = objectArray2.array.index(of: someOtherObject)
let matchingObject = objectArray2.array[indexOfSomeOtherObject]
matchingObject.propertyArray.append("TestString")
print("\(matchingObejct.propertyArray)") //prints []
}
}
Why doesn't it print ["TestString"]? The same goes for when I remove one of the values from the array, the update doesnt occur.
If you are wondering why I am doing this, it's because the objects are modified in the second vC and the UI of the tableView cells in the first vC are dependent upon the properties of the objects. Hence why my data is represented as a class (reference type).
Update your someFunc():
func someFunc() {
//try to find the same object
let indexOfMatchingObject = objectArray2.array.index(of: someOtherObject)
//obtain this common objec from array
let matchingObject = objectArray2.array[indexOfSomeOtherObject]
//update propertyArray from matchingObject
matchingObject.propertyArray.append("TestString")
}
Also update your first VC table view in viewWillApear.
I realised that the answer to my question is nothing to do with the class I had created or the segue, but is in actual fact to do with the state of the array. You cannot append to an array that has not been initialised. In order to append to it, you must first initialise it, then you may append to it. Playground code has been provided below to demonstrate this in the context of the question above:
//Create object class
class Object: Equatable {
var propertyArray: [String]?
var id: Int = Int()
static func ==(lhs: Object, rhs: Object) -> Bool {
return lhs.id == rhs.id
}
}
//Create class that will hold array of objects
class ArrayOfObjects {
var array: [Object] = []
}
//initialise class that will hold array of objects
var array = ArrayOfObjects()
//set the initial values for the array
func setArray() {
let object1 = Object()
object1.id = 1
object1.propertyArray = ["hello"]
let object2a = Object()
object2a.id = 2
object2a.propertyArray = ["bye"]
array.array = [object1, object2a]
}
setArray()
//Create new object that will be used to match with one already in the array
let object2b = Object()
object2b.id = 2
object2b.propertyArray = ["bye"]
//Find if the new object exists in the array
let index = array.array.index(of: object2b)
let matchingObject = array.array[index!]
matchingObject.propertyArray?.append("welcome")
//We were able to append to the matchingObject (object2a) because the property array had been initialised
print(matchingObject.propertyArray) //prints ["bye", "welcome"]
//Create new object with uninitialised propertyArray
let object3a = Object()
object3a.id = 4
//Append this new object to the array
array.array.append(object3a)
//Create another new object that will be used to match with object3a
var object3b = Object()
object3b.id = 4
//Find if object3b can be matched in the array
let index2 = array.array.index(of: object3b)
let matchingObject2 = array.array[index2!]
matchingObject2.propertyArray?.append("hello")
//A match was found for object3b, but the array had not been initialised and so we couldn't append to it
print(matchingObject2.propertyArray) //prints nil
//Initialise the array
matchingObject2.propertyArray = []
matchingObject2.propertyArray?.append("goodbye")
//We can now append to it as it has been initialised
print(matchingObject2.propertyArray) //prints ["goodbye"]
In my UIViewController I call a method in another class which returns me a list of NSManagedObjects, which I instantiate in an array.
Here is the code:
fileprivate var albumList = [Album]()
private func loadAlbums() {
APIHandler().getUsersAlbums() {
albums in
self.albumList = albums
self.collectionView?.reloadData()
}
}
But this was causing my array to have nil properties once loadAlbums was finished and APIHandler's instance cleared. I solved this for now by having an instance of APIHandler on my UIViewController and calling the function from there, like this:
let api = SpotifyAPIHandler()
fileprivate var albumList = [Album]()
private func loadAlbums() {
api.getUsersAlbums() {
albums in
self.albumList = albums
self.collectionView?.reloadData()
}
}
Still I am not happy with this. Why does this happen?
How can I instantiate a completely new list?
I think what's happening is you're creating a new instance of APIHandler every time you're calling the loadAlbums() func instead what you can do is have a static refrence of ur APIHander in the APIHander class like so
class APIHander: NSObject {
static let handler = APIHander()
}
Then you would call your API like this
private func loadAlbums() {
APIHandler.handler.getUsersAlbums() {
albums in
self.albumList = albums
self.collectionView?.reloadData()
}
}
class MySingleton{
static let shareInstance = MySingleton()
private init() {}
var myDetail = [Detail]()
}
class DetailTableViewController {
var expense = [Detail]()
override func viewDidLoad() {
super.viewDidLoad()
... put stuff in expense array ....
MySingleton.shareInstance.myDetail = expense //<--- doesn't work
// error is "cannot assign value of type '[Detail]' to type [MySingleton.Detail]"
}
}
How do I copy an array to my MySingleton?
right now i just pass my array around my classes using segue
From your error, it is likely you are defining Detail twice, once locally to the singleton, once globally for the viewController.
I am trying to use the array of strings that is named "theList" in another class in my project. I understand that the variable is declared within a method and therefore not of global scope. But how can I fix that? I have tried a few things and nothing is working. What is the best way to accomplish this?
EDIT: All I want to do is set "theList" equal to "Body"
[![enter image description here][1]][1]
I hope this clears it up a little bit, and I can select an answer. Thanks to everyone!
I think if you want to access that list outside of the function you should simply make it a variable of the class.
class TaskManager: NSObject {
//Sets up array of Tasks
var tasks = [task]()
var theList = [String]()
//Add Task Function
func addTask(serial: String){
tasks.append(task(serial: serial))
theList = tasks.map({ (task) -> String in
return task.serial
})
}
}
Your best way to do this is to look at the scope of your class and the declaration. Define your list in a place that has scope everywhere you want to use it, or make it global. That's how I do this kind of task. Sometimes add it to a higher level class declaration, and sometimes I make it global to, say, the entire program. Depends on the task and my outlook at the time of coding.
Let you function return the results of adding the task then:
class TaskManager: NSObject {
// declare your variables
func addTask(serial: String) -> [String] {
// put your realisation
}
}
or make your variable with serials available publicly:
class TaskManager: NSObject {
var tasks = [Task]()
public var serials = [String]()
func addTask(serial: String) {
// put your realisation
// save the results to serials
}
}
and then access it via the instance.
Adding theList Variable above class made it global.
Then I had to remove let from the addTask function.
//Add Task Function
func addTask(serial: String){
tasks.append(task(serial: serial))
let theList = tasks.map({ (task) -> String in
return task.serial
}).joinWithSeparator("\n")
Became
//Add Task Function
func addTask(serial: String){
tasks.append(task(serial: serial))
theList = tasks.map({ (task) -> String in
return task.serial
}).joinWithSeparator("\n")
The final code is as follows.
import UIKit
var theList : String = String()
var taskMgr: TaskManager = TaskManager()
struct task {
var serial = "Un-Named"
}
public class TaskManager: NSObject {
//Sets up array of Tasks
var tasks = [task]()
//Add Task Function
func addTask(serial: String){
tasks.append(task(serial: serial))
theList = tasks.map({ (task) -> String in
return task.serial
}).joinWithSeparator("\n")
do {
//try tasksString.writeToFile(pathToFile, atomically: true, encoding: NSUTF8StringEncoding)
print(theList)
}
}
}
I selected the answer. Thank you to all who helped cure my tired eyes.
How can I write a function in a separate swift file and use it (import it) to my ViewController.swift file? I have written a lot of code and all of the code is in the ViewController.swift file, I really need to make this look good and place functions on separate files, for cleaner code. I have functions dealing with parsing HTML, functions dealing with ordering results, presenting results, responding to user actions, etc. Many thanks for any help!
if let htmlString = String(contentsOfURL: checkedUrl, encoding: NSUTF8StringEncoding, error: nil) {
// Parsing HTML
let opt = CInt(HTML_PARSE_NOERROR.value | HTML_PARSE_RECOVER.value)
var err : NSError?
var parser = HTMLParser(html: htmlString, encoding: NSUTF8StringEncoding, option: opt, error: &err)
var bodyNode = parser.body
// Create an array of the part of HTML you need
if let inputNodes = bodyNode?.findChildTags("h4") { //inputNodes is an array with all the "h4" tag strings
for node in inputNodes {
let result = html2String(node.rawContents)
println("Nyheter: \(result)")
}
}
When I add that function to a separate swift file, how can I use it in my ViewDidLoad method using a "shorthand"? A short keyword that grabs that chunk of code and use it?
Easy. You just create a new Swift file into your Xcode project (File - New - File - Swift file or just ⌘-N) and put your functions and classes there. No need to import anything in your view controller file as both files are part of the same package and thus see each others functions and types (unless marked as private).
func parseHtml(url: NSURL) -> String { ... your code goes here ... }
You need to use singletons.
Create NSObject in User.swift
import Foundation
import UIKit
class User: NSObject {
var name: String = 0
func getName() -> String{
name = "Erik Lydecker"
return name
}
}
Then initialize your object and trigger the method there.
ViewController.swift
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let instanceOfUser = User()
instanceOfUser.getName() // Erik Lydecker
}
}
You can create a Utils class, filled with static variables/functions
For example:
class Utils {
static func convert(to variable: String) -> Int? {
... do some stuff to variable
return newVariable
}
}
// on your ViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
if let converted = Utils.convert(to: "123") {
print(converted)
}
}
By making use of static functions you can access them anywhere and everywhere to reuse them.
------------------
Another way is to make use of extensions
For example:
extension String {
var toInt: Int? {
return Int(self)
}
}
// on your ViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
if let converted = "SomeValue".toInt {
print(converted)
}
}
Both of these can be used in many different scenarios.