I want to access stringEncoded in viewDidLoad but I cannot. How do I access it. Is it because it is not in main queue or is it about global variables staff?
I try new codes but still it doesn't work...
//Editted.
According to the code your provided, the var stringEncoded is defined into your jsonGetir function, while you want to access it from your viewDidLoad
So the solution is to put this variable in the class level, like this:
class YourClass{
var stringEncoded = "asomthing" //change the value or the type to whatever suites u
viewDidLoad{}
func jsonGetir(){}
}
Update
As you requested, a full working example:
import UIKit
class StViewController: UIViewController {
var stringEncoded = [String]()
func jsonGetir() {
let urls = NSURL(string: "http://gigayt.com/mackolik/deneme.php")
let sessions = NSURLSession.sharedSession().dataTaskWithURL(urls!){
data, response, error -> Void in
if (error != nil){
print(error)
}
do {
if let jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options: []) as? NSDictionary {
let x = jsonResult["kodlar"] as! String
self.stringEncoded = x.componentsSeparatedByString(",")
}
}
catch {
print(error)
}
dispatch_async(dispatch_get_main_queue()){
}
}
sessions.resume()
}
override func viewDidLoad() {
super.viewDidLoad()
for i in 0..<self.stringEncoded.count {
print(self.stringEncoded[i])
}
}
}
First declare the variable in class scope. If you want persist the that parameter then you can use inout parameter. Please refer to get more about inout parameter, hope you understand
Related
In my swift code below I am trying to save a string into core data using a helper class.
Right now my code is causing a runtime error stating Cannot assign value of type 'Data' to type 'String?' at imageInstance.text = data. I did something similar trying to save a image and it worked. I also added a core data photo
core data pic
class DataBaseHelper {
static let shareInstance = DataBaseHelper()
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
func saveText(data: Data) {
let imageInstance = Info(context: context)
imageInstance.txt = data
do {
try context.save()
print("text is saved")
} catch {
print(error.localizedDescription)
}
}
}
BASE CLASS
class ViewController: UIViewController, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
DataBaseHelper.shareInstance.saveText(data: "Jessica")}
Please be more careful how you name your functions.
You are going to save Text( so the parameter label data is misleading and the type Data is wrong.
Replace
func saveText(data: Data) {
with
func saveText(data: String) {
or – more meaningful because text is already a part of the function name
func saveText(_ string: String) {
let imageInstance = Info(context: context)
imageInstance.txt = string
and call it
DataBaseHelper.shareInstance.saveText("Jessica")
I´ve a webrequest with jsonserialization, after that, a for-in fetch process.
In whole this takes approximately 5-7 seconds.
After that i want to refersh my tableview in Viewcontroller.
The scheme of the function looks like this.
public struct Container {
let name: String
let symbol: String
let rank: String
}
public var dataArray = [Container]()
func fetchNewData() {
var view = ViewController()
// WebbRquest...
// Json serialization...
// the following list is much longer, will take a while...
for items in json {
let name = items["name"] as? AnyObject;
let symbol = items["symbol"] as? AnyObject;
let rank = items["rank"] as? AnyObject;
let result = Container(name: name! as! String, symbol: symbol! as! String,rank: rank! as! String)
dataArray.append(result)
}
// Now, after alle the work is done, i want to reload the tableview in Viewcontrller:
view.reload()
// Here i´m getting error, because nothing will be executed after return.
}
How can I call the reload function, after the webrequest process is finished? Because after the return, the function doesn´t execute anything anymore.
And no other function will "know" when the fetchNewData() function is finished.
Thanks for any help!
#IBAction func updateButton(_ sender: Any) {
fetchNewData()
}
According Phillipps suggestion, I had to modify the #IBAction func a little bit.
But now it´s working. Awesome!
Here the full working version:
public struct Container {
let name: String
let symbol: String
let rank: String
}
public var dataArray = [Container]()
func fetchNewData(completion:#escaping ([Container])->()) {
var view = ViewController()
// WebbRquest...
// Json serialization...
// the following list is much longer, will take a while...
for items in json {
let name = items["name"] as? AnyObject;
let symbol = items["symbol"] as? AnyObject;
let rank = items["rank"] as? AnyObject;
let result = Container(name: name! as! String, symbol: symbol! as! String,rank: rank! as! String)
dataArray.append(result)
}
completion(dataArray)
}
This is the actionFunc:
#IBAction func upDateButton(_ sender: Any) {
let data = dataArray
fetchNewData() {_ in (data)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
Here's a start. It will be vague because I'm making guesses about code I can't see, but you may be able to convert it to your own needs.
Change the fetch function so that it takes a closure as a parameter:
func fetchNewData(completion:([Container])->()) {
...note that the closure will accept the data array when it's called.
After you have your json all parsed, you then invoke the closure:
dataArray.append(result)
}
completion(dataArray)
The "magic" is in the view controller where you tell fetchNewData what to do when it's finished. Something like:
#IBAction func updateButton(_ sender: Any) {
fetchNewData() {(data)
// Save the data where the view controller can use it
self.tableArray = data
// Main queue for UI update
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
Note that the closure is written in the view controller, so self is the view controller. This means no need to create a second (useless) controller inside the fetch.
I have created a helper class SignUpSetUp in order to sign in to my app instead of reusing code. In this class I have a private function waitForElementToAppear to wait for elements in the testing suite to appear. However when using this function it is generating the error:
Cannot convert value of type 'XCUIElement' to expected argument type 'SignUpSetUp'
Why is this and how do I resolve this?
My code is:
import XCTest
class SignUpSetUp: XCTestCase {
var systemAlertMonitorToken: NSObjectProtocol? = nil
static let signUpApp = XCUIApplication()
static let app = XCUIApplication()
class func signUpApp() {
// XCUIApplication().launch()
//signUpSetUp.launch()
sleep(2)
let element = app.buttons["Enable notifications"]
if element.exists {
element.tap()
}
sleep(3)
let notifcationsAlert = self.app.alerts.buttons["OK"]
if notifcationsAlert.exists{
notifcationsAlert.tap()
notifcationsAlert.tap()
}
sleep(2)
waitForElementToAppear(self.app.tabBars.buttons["Nearby"])
let nearbyTab = self.app.tabBars.buttons["Nearby"]
if nearbyTab.exists {
nearbyTab.tap()
}
sleep(2)
let enableLocation = self.app.buttons["Enable location"]
if enableLocation.exists {
enableLocation.tap()
}
let allowLocation = self.app.alerts.buttons["Allow"]
if allowLocation.exists {
allowLocation.tap()
allowLocation.tap()
}
sleep(2)
//waitForElementToAppear(self.app.tabBars.buttons.elementBoundByIndex(4))
let settingsButton = self.app.tabBars.buttons.elementBoundByIndex(4)
XCTAssert(settingsButton.exists)
settingsButton.tap()
let signUpButton = self.app.tables.staticTexts["Sign Up"]
if signUpButton.exists {
signUpButton.tap()
}
}
private func waitForElementToAppear(element: XCUIElement,
file: String = #file, line: UInt = #line) {
let existsPredicate = NSPredicate(format: "exists == true")
expectationForPredicate(existsPredicate,
evaluatedWithObject: element, handler: nil)
waitForExpectationsWithTimeout(5) { (error) -> Void in
if (error != nil) {
let message = "Failed to find \(element) after 5 seconds."
self.recordFailureWithDescription(message,
inFile: file, atLine: line, expected: true)
}
}
}
Your problem is that you are calling an instance method from a class method.
waitForElementToAppear is an instance method, but signUpApp is a class method. In order for your code to work, you need to align the two. Remove class from the signature of signUpApp, and also remove static from your two properties, and change references to self.app to just app.
let signUpApp = XCUIApplication()
let app = XCUIApplication()
func signUpApp() { ... }
Unless you do want the methods to be on a class/static level, in which case you can align in the other direction.
In terms of best practices, there's no need to have two properties holding an instance of XCUIApplication - just have one and use that as they will both function in the same way.
I would like to call a function which is coded on another class.
So far I have made a struct on the file structs.swift for my data:
struct defValues {
let defCityName: String
let loadImages: Bool
init(defCity: String, loadImgs: Bool){
self.defCityName = defCity
self.loadImages = loadImgs
}
}
I have made the file Defaults.swift containing:
import Foundation
class DefaultsSet {
let cityKey: String = "default_city"
let loadKey: String = "load_imgs"
func read() -> defValues {
let defaults = NSUserDefaults.standardUserDefaults()
if let name = defaults.stringForKey(cityKey){
print(name)
let valuesToReturn = defValues(defCity: name, loadImgs: true)
return valuesToReturn
}
else {
let valuesToReturn = defValues(defCity: "No default city set", loadImgs: true)
return valuesToReturn
}
}
func write(city: String, load: Bool){
let def = NSUserDefaults.standardUserDefaults()
def.setObject(city, forKey: cityKey)
def.setBool(load, forKey: loadKey)
}
}
in which I have the two functions read, write to read and write data with NSUsersDefault respectively.
On my main ViewController I can read data with:
let loadeddata: defValues = DefaultsSet().read()
if loadeddata.defCityName == "No default city set" {
defaultCity = "London"
}
else {
defaultCity = loadeddata.defCityName
defaultLoad = loadeddata.loadImages
}
But when I try to write data it gives me error. I use this code:
#IBOutlet var settingsTable: UITableView!
#IBOutlet var defaultCityName: UITextField!
#IBOutlet var loadImgs: UISwitch!
var switchState: Bool = true
#IBAction func switchChanged(sender: UISwitch) {
if sender.on{
switchState = true
print(switchState)
}else {
switchState = false
print(switchState)
}
}
#IBAction func saveSettings(sender: UIButton) {
DefaultsSet.write(defaultCityName.text, switchState)
}
You need an instance of the DefaultsSet class
In the view controller add this line on the class level
var setOfDefaults = DefaultsSet()
Then read
let loadeddata = setOfDefaults.read()
and write
setOfDefaults.write(defaultCityName.text, switchState)
The variable name setOfDefaults is on purpose to see the difference.
Or make the functions class functions and the variables static variables and call the functions on the class (without parentheses)
From the code you posted, it seems you either need to make the write method a class method (just prefix it with class) or you need to call it on an instance of DefaultsSet: DefaultsSet().write(defaultCityName.text, switchState).
Another issue I found is that you also need to unwrapp the value of the textField. Your write method takes as parameters a String and a Bool, but the value of defaultCityName.text is an optional, so String?. This results in a compiler error.
You can try something like this:
#IBAction func saveSettings(sender: UIButton) {
guard let text = defaultCityName.text else {
// the text is empty - nothing to save
return
}
DefaultsSet.write(text, switchState)
}
This code should now compile and let you call your method.
Let me know if it helped you solve the problem
I am trying to create a common class for storing and retrieving data in Parse. I made the ParseProcessing class a singleton class. From my main View Controller I load the data and store it into a dictionary in the ParseProcessing. I do this by creating a shared instance of the ParseProcessing class. From another view controller I try to access the data from the dictionary. I assumed that because ParseProcessing is a singleton class that I have a single copy of the dictionary. This does not appear to be correct. How should I declare the variables inside the ParseProcessing so that they are shared? The code is shown below:
import UIKit
var gSep = ","
class QwikFileViewController: UIViewController {
var loadData = ParseProcessing.sharedInstance
override func viewDidLoad() {
super.viewDidLoad()
// load data from Parse
loadData.loadCategorySubcategoryData()
loadData.loadRecordsFromParse()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
ParseProcessing Singleton Class
import UIKit
import Parse
class ParseProcessing: Parse {
var dictMenuList = [String:String]()
var noteTitle = [String]()
var notes = [String]()
var thumbnailFiles = [PFFile]()
var objectIds = [String]()
var noteImage = UIImage()
class var sharedInstance:ParseProcessing {
struct singleton {
static let instance:ParseProcessing = ParseProcessing()
}
return singleton.instance
}
// Load Category/Subcategory data from Parse Data Base
func loadRecordsFromParse () -> Bool{
var tmpFile = [PFFile]()
var loadComplete = false
var query = PFQuery(className:"Record")
query.findObjectsInBackgroundWithBlock {
(objects, error) -> Void in
if error == nil {
// The find succeeded.
println("Successfully retrieved \(objects!.count) items.")
for object in objects! {
self.noteTitle.append(object["title"] as! String)
self.notes.append(object["notes"] as! String)
self.thumbnailFiles.append(object["thumbnail"] as! PFFile)
self.objectIds.append(String(stringInterpolationSegment: object.objectId))
}
} else {
println("\(error)")
}
loadComplete = true
}
return loadComplete
}
// Load Category/Subcategory data from Parse Data Base
func loadCategorySubcategoryData () // -> Dictionary <String,String>
{
var success : Bool = false
var d : Dictionary <String,String> = ["":""]
var menu = PFQuery(className: "Classification")
println("ParseProcessing: loadCategory...")
menu.findObjectsInBackgroundWithBlock {
(objects, error) -> Void in
if error == nil {
var category = ""
var subcategory = ""
for object in objects! {
category = object["category"] as! String
println("ParseProcessing: category = \(category)")
subcategory = object["subcategory"] as! String
println("ParseProcessing: subcategory = \(subcategory)")
d[category] = subcategory
}
success = true
self.dictMenuList = d
return
} else {
println("ParseProcessing: error = \(error)")
success = false
}
}
return
}
}
Another View Controller to examine the data
import UIKit
class TestViewController: UIViewController {
var dictMenuList = [String:String]()
var loadData = ParseProcessing.sharedInstance
override func viewDidLoad() {
super.viewDidLoad()
dictMenuList = loadData.dictMenuList
println("dictMenuList: \(dictMenuList)")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
The problem is that findObjectsInBackgroundWithBlock is asynchronous method (i.e. it returns immediately but the closure is called later when the query is done). So you cannot return loadComplete in loadRecordsFromParse, for example. This background request will almost certainly never be done by the time loadRecordsFromParse returns.
Instead, you probably want to adopt the completionHandler pattern. For example, this sample loadRecords doesn't try to return anything immediately, but rather will call the completionHandler when the request is done.
func loadRecords(completionHandler:([SomeObject]?, NSError?) -> ()) {
let query = PFQuery(className: "SomeClass")
query.findObjectsInBackgroundWithBlock { objects, error in
// build some model object
completionHandler(objectArray, error)
}
}
And you'd call it like so:
loadData.loadRecords() { objects, error in
// use `objects` (and make sure `error` is `nil`) here
}
// but do not use those variables here, as the above closure probably has not run yet!
Frankly, I'd be inclined to get rid of those properties in your singleton altogether. When you're dealing with asynchronous code, to have public properties that are updated asynchronously is going to be a source of heartache. You can do it, but it wouldn't be my first choice.
For example, when TestViewController is presented, you cannot assume that the asynchronous fetch associated with dictMenuList is done yet. I look at this and wonder if it makes sense for TestViewController to initiate the fetch itself and then use dictMenuList in the completion handler. That's going to be easiest.
If you must initiate the asynchronous request from one view controller and then have another view controller be informed when that asynchronous request is done, then you might have to use some other pattern, such as notifications (e.g. use NSNotificationCenter, and have the singleton post notifications when the various requests are done, and then any view controller that needs to be informed of this fact can add themselves as observers for that notification).