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.
Related
So, I've been working on a Weather App with the following brief Data Model
class CurrentWeather
{
private var _cityName: String!
private var _date: String!
private var _weatherType: String!
private var _currentTemp: Double!
var cityName: String
{
if _cityName == nil
{
_cityName = ""
}
return _cityName
}
// Same idea for getters var date, var weatherType and
// var currentTemp (returns 0.0 if it is nil)
// Not showing that here
func downloadWeatherDetails(completed: DownloadComplete)
{
// Function which computes values though a url and stores in instance variables
// Not showing the entire actual function here
self._cityName = name.capitalized. // value computed earlier
print(self._cityName)
self._weatherType = main.capitalized // value computed earlier
print(self._weatherType)
self._currentTemp = currentTemp - 273.15 // value computed earlier
print(self._currentTemp)
completed()
}
}
where the type DownloadComplete is a type alias to ()->()
In the main ViewController.swift, I have created an object and called this function (with trailing closure syntax)
var currentWeather: CurrentWeather!
override func viewDidLoad()
{
super.viewDidLoad()
currentWeather = CurrentWeather()
currentWeather.downloadWeatherDetails {
self.updateMainUI() // I have created this function
}
}
func updateMainUI()
{
dateLabel.text = currentWeather.date
currentTempLabel.text = String(currentWeather.currentTemp)
locationLabel.text = currentWeather.cityName
currentWeatherTypeLabel.text = currentWeather.weatherType
currentWeatherImage.image = UIImage(named: currentWeather.weatherType)
print("Tested: \(currentWeather.currentTemp)")
print("Tested: \(currentWeather.cityName)")
print("Tested: \(currentWeather.weatherType)")
}
So the Expected output:
Logically,
I have created a CurrentWeather object
Called the downloadWeatherDetails function which should load the different computed values in the private vars.
Call the user defined updateMainUI function which displays the different values on my app's UI
So the output should be like
Birim. //cityname
Clear. //weatherType
29.134 //currentTemp
Tested: 29.134
Tested: Birim
Tested: Clear
But the output which I get is
Tested: 0.0
Tested: (indicating "")
Tested: (indicating "")
Birim
Clear
29.134
So, basically the functions downloadWeatherDetails and updateMainUI are called in the wrong order? Why is this so? Is this somehow related to asynchronous execution of functions?
I have tried not using the trailing closure, but it still doesn't work.
I also tried leaving the closure empty and calling updateMainUI after downloadWeatherDetails call like this
currentWeather.downloadWeatherDetails {
}
self.updateMainUI()
But this too doesn't work. Any ideas of why the functions are called in the wrong order?
UPDATE:
the underscore variables are private vars while the non-underscore variables are the getters like
var cityName: String
{
if _cityName == nil
{
_cityName = ""
}
return _cityName
}
// Same idea for getters var date, var weatherType and
// var currentTemp (returns 0.0 if it is nil)
// Not showing that here
UPDATE 2:
Project files are here(in case one may want to refer): https://github.com/danny311296/Weather-App
You must call your 'completed()' function within the Alamofire request callback. Since the request function is asynchronous it does not wait for it to finish before executing completed().
Alamofire.request(CURRENT_WEATHER_URL).responseJSON { response in
// handle response...
// when done call completed
completed()
}
I guess the issue is that your "updateMainUI" method is being called before the completion of download process. Although you have implemented the Completion Listener, this should be working but I don't what's wrong with that. Try to use some other methods like delegation or notification to observe the downloading processes.
Check this link to see other method to observe completion:
https://medium.com/ios-os-x-development/ios-three-ways-to-pass-data-from-model-to-controller-b47cc72a4336
In Swift I have this Singleton
struct Networking {
static let shared = Networking()
private var observed: Set<String> = []
}
I have to manipulate observed and I need to create useful method to insert and remove member in Set.
mutating func addObserver(for member: String) {
//other code
observed.insert(member)
}
mutating func removeObserver(for member: String) {
//other code
observed.remove(member)
}
The problem is when I try to call this methods like this
Networking.shared.addObserver(for: "x")
because I'm getting this error
cannot use mutating on immutable value: “shared” is a “let” constant
This error is pretty clear. shared is let and obviously it cannot be modified. But to modify the var I need to declare method as mutating. It's a vicious circle.
Any ideas?
Thanks.
If you want your Networking object to act as a singleton, why not make it a class instead of a struct?
class Networking {
static let shared = Networking()
private var observed: Set<String> = []
func addObserver(for member: String) {
//other code
observed.insert(member)
}
func removeObserver(for member: String) {
//other code
observed.remove(member)
}
}
Networking.shared.addObserver(for: "x")
This simplifies the code and solves your issue.
Basically your syntax is wrong, Networking() creates a new instance of the class.
To use the struct as singleton you have to write
Networking.shared.addObserver(for: "x")
Then declare shared as mutable
static var shared = Networking()
There is also another way of doing it
class Networking {
static let shared = Networking()
var observed: Set<String> = [] {
didSet {
print("set has changed")
}
}
}
Value Type
Since Set is a struct (a value type), the didSet block will be executed every time you add or remove and element to Set.
Networking.shared.observed.insert("a")
set has changed
Networking.shared.observed.insert("b")
set has changed
Networking.shared.observed.remove("a")
set has changed
I have the following function in a class in my program:
func getXMLForTrips(atStop: String, forRoute: Int, completionHandler: #escaping (String) -> Void) {
let params = [api key, forRoute, atStop]
Alamofire.request(apiURL, parameters: params).responseString { response in
if let xmlData = response.result.value {
completionHandler(xmlData)
} else {
completionHandler("Error")
}
}
}
In the init() for the class, I attempt to call it like this:
getXMLForTrips(atStop: stop, forRoute: route) { xmlData in
self.XMLString = xmlData
}
This compiles without errors, but after init() is executed, my class's self.XMLString is still nil (shown both by the Xcode debugger and by my program crashing due to the nil value later on). I see no reason why this shouldn't work. Can anyone see what I am missing?
You shouldn't be making internet calls in the initializer of a class. You will reach the return of the init method before you go into the completion of your internet call, which means it is possible that the class will be initialized with a nil value for the variable you are trying to set.
Preferably, you would have another class such as an API Client or Data Source or View Controller with those methods in it. I am not sure what your class with the init() method is called, but lets say it is called Trips.
class Trips: NSObject {
var xmlString: String
init(withString xml: String) {
xmlString = xml
}
}
Then one option is to put the other code in whatever class you are referencing this object in.
I'm gonna use a view controller as an example because I don't really know what you are working with since you only showed two methods.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
//setting some fake variables as an example
let stop = "Stop"
let route = 3
//just going to put that method call here for now
getXMLForTrips(atStop: stop, forRoute: route) { xmlData in
//initialize Trip object with our response string
let trip = Trip(withString: xmlData)
}
}
func getXMLForTrips(atStop: String, forRoute: Int, completionHandler: #escaping (String) -> Void) {
let params = [api key, forRoute, atStop]
Alamofire.request(apiURL, parameters: params).responseString { response in
if let xmlData = response.result.value {
completionHandler(xmlData)
} else {
completionHandler("Error")
}
}
}
}
If you want to be able to initialize the class without requiring setting the xmlString variable, you can still do the same thing.
Change the Trips class init() method to whatever you need it to be and set var xmlString = "" or make it optional: var xmlString: String?.
Initialize the class wherever you need it initialized, then in the completion of getXMLForTrips, do trip.xmlString = xmlData.
I am having users input a list of "serials" into a TableView called "Equipment". Here is the code for the tasks array.
var taskMgr: TaskManager = TaskManager()
struct task {
var serial = "Un-Named"
//var desc = "Un-Named"
}
class TaskManager: NSObject {
//Sets up array of Tasks
var tasks = [task]()
//Add Task Function
func addTask(serial: String){
tasks.append(task(serial: serial))
}
}
I want to take the table data and export it to a .txt file or .csv with each task on separate lines. What is the best way to do this? Please help I've been stuck for a few days. I'm not sure which way I should approach this.
Thanks!
String.writeToFile should be able to do this for you if you map your task array into a string array and then join it into one string with new line as separators
let tasksString = tasks.map({ (task) -> String in
return task.serial
}).joinWithSeparator("\n")
do {
try tasksString.writeToFile(pathToFile, atomically: true, encoding: NSUTF8StringEncoding)
} catch {
}
I have following code working in a playground. The didSet observer is working as expected.
struct itemStruct {
var name : String = "" {
didSet (newName) {
didSetNameTest(name)
}
}
}
func didSetNameTest (name : String) {
println("didSet itemStruct: \(name)")
}
var item = itemStruct()
item.name = "test"
If I move the code within a class I get an compiler error:
class itemClass {
struct classItemStruct{
var name : String = "" {
didSet(newName) {
didSetClassNameTest(name)
}
}
}
func didSetClassNameTest(name:String) {
println("didSet itemClass: \(name)")
}
var structItem = classItemStruct()
}
var cItem = itemClass()
cItem.structItem.name = "Test"
Error: Cannot invoke 'didSelectClassNameTest' with an argument list of type '(String)'.
All code could be copied in playground.
Since instance of an inner class is independent of any instance of the outer class as described in a link of #ABakerSmith comment a possible workaround for this would be by makeing didSetClassNameTest function private and static and then call it statically itemClass.didSetClassNameTest(name) on didSet method
Inner types are independent of their outer types. Unlike in java classItemStruct knows nothing about its outer type itemClass.