Global variable in swift 3 - swift3

I have the variable provincias_ws which has the answer of a service, so far everything is fine but I want to know how I make the variable provincias_ws is global since I want to print it in the textFieldDidBeginEditing method
class ViewCtrl: UIViewController, ValidationDelegate, UITextFieldDelegate {
#IBOutlet weak var txt_ciudadU: SearchTextField!
override func viewDidLoad() {
super.viewDidLoad()
opciones_autocompletado()
txt_ciudadU.delegate = self
}
func opciones_autocompletado(){
txt_provinciaU.inlineMode = true
txt_ciudadU.inlineMode = true
Services.objServices.ServProv().then{
data -> Void in
let res = JSON(data)
let status = res["status"].boolValue
if(status){
let provincias_ws = res["data"]["provincias"] //How to make this variable global
}else{
let error = res["error"]["error_data"].stringValue
print(error)
}
SVProgressHUD.dismiss()
UIApplication.shared.endIgnoringInteractionEvents()
}.catch{
error -> Void in
SVProgressHUD.dismiss()
UIApplication.shared.endIgnoringInteractionEvents()
}
}
func textFieldDidBeginEditing(_ textField: UITextField) {
print(provincias_ws)
}
}
Thank you in advance

One can create a "global" variable (meaning it can be access from anywhere in the project) by defining it outside of the class block.
However, it looks like what you want is a class instance variable.
Your text field txt_ciudadU is already an example of an instance variable or property of the class ViewCtrl.
This would be the same, just without #IBOutlet weak.
So would probably be (assuming it's a String):
var provincias_ws: String?
Note the ? declares it as an optional value because it will not be set until it gets set inside your block. You could also initialize it to some default or empty value (e.g. var provincias_ws = "") or you could use ! to indicate you won't be trying to unwrap it when it's nil, although it's better not to rely on that because it is error-prone. What you choose to do depends on your needs.
And you would put that at the same hierarchical level as your text field variable (i.e. right above or below it, although it could go anywhere really, as long as it's not inside a function or another block).
And you would get rid of the current let next to provincias_ws and prepend it with self. i.e.:
self.provincias_ws = res["data"]["provincias"]
The self. is necessary because it's inside a block and the compiler needs that for context.

Related

How to update variable in List For Each

struct NotesView: View {
#State var notesArray = [Note]()
public var deleteid: String
var body: some View {
List{
ForEach(notesArray, id: \._id) { notesArray in
NavigationLink(destination: AddNotesView(addNotesViewIdentifier: "updateNote", id: notesArray._id, title: notesArray.title, note: notesArray.note, noteDate: notesArray.date)){
HStack {
Text(notesArray.title)
deleteid = notesArray._id //ERROR - Cannot assign to property: 'self' is immutable
}
}
}
.onDelete(perform: deleteNoteAtIndex)
}
}
func deleteNoteAtIndex(at offsets: IndexSet){ APIFunctions.functions.DeleteNote(id: _id) }
I was expecting the variable "deleteid" to update.
I assumed you can modify any variable by calling that variable and set it equal to a new value.
Like this
First declare variable:
var deleteid: String
next modify variables string valve
deleteid = notesArray._id
A couple of things:
This isn't directly related to your question, but may help you navigation your own code better... When you create a ForEach view to iterate over an array, you should use a different name for the value that represents each element in the iteration. Here, you're using the name notesArray for your array of notes, then creating a second local variable called notesArray for the loop. That variable inside the block will be an instance of Note, so I'd name it note, e.g.:
ForEach(notesArray, id: \._id) { note in
NavigationLink(destination: AddNotesView(addNotesViewIdentifier: note._id, // etc
}
If you want variables to be modifiable inside views, they should be #State variables. This is important due to the way Swift struct lifecycles work, and how the SwiftUI rendering system loads and reloads structs as it works out what has changed.
I'm not entirely sure what deleteid is supposed to represent here, and it's possible you don't need it at all. If you're using the onDelete modifier to implement SwiftUI's native swipe-to-delete system, SwiftUI will give you an IndexSet, which is a collection (usually of just one) of the positions of the item(s) to delete in the array.
From there, you can find the item(s) at each index and then either remove them, or lookup some other value (e.g., their _id attribute) and do some other operation on them.
So the method you might call in onDelete could look something like:
func deleteNoteAtIndex(offsets: IndexSet) {
// get the array objects that the offsets point to
let notes = offsets.map { noteArray[$0] }
for note in notes {
APIFunctions.functions.deleteNote(id: note._id)
}
}

Pass a published property as binding

I have an ObservableObject with a published dictionary of strings to arrays with arrays with Ints:
class MyObservableObject: ObservableObject {
#Published var myDict: [String: [[Int]]]
}
And I want to pass one array of Ints as a Binding from inside the same class to a function of a different struct:
{
...
func someFunc(key: String, index: Int) {
someStruct.func(myDict[key]![index])
}
...
}
I understand that #Published vars can't be passed as Bindings. I'm still hoping that there's any way to achieve this. I also tried storing a reference to the array in the other struct using an inout variable, also without success.
#Published vars can't be passed as Bindings
It is not true - it is possible, via projected value using '$', but you want to pass not a property, but part of value of a property, and this is different thing.
The context is not clear and this someFunc smells not-well :) - I'd say it is needed some refactoring here, but, anyway, technically it is possible to do what you want using dynamically in-place generated binding, like
func someFunc(key: String, index: Int) {
guard myDict[key] != nil else { return }
someStruct.func(Binding<[Int]>(
get: { self.myDict[key]![index] },
set: { self.myDict[key]![index] = $0 }
))
}

SwiftUI "Cannot use instance member 'numberOfDevice' within property initializer; property initializers run before 'self' is available" error

The bolded line (ie var text: String...) gives a "Cannot use instance member 'numberOfDevice' within property initializer; property initializers run before 'self' is available" error. Do I need an init? If so where? Is there a different solution?
struct PairView: View {
var theClass = BluetoothManager()
init() {theClass.viewDidLoad()}
var body: some View {
List {
ForEach(0..<BluetoothManager.peripheralArray.count) { number in //iterates thru 0 to array's count
ConnectionView(numberOfDevice: number) // create a ConnectionView for each number
}
}
}
}
//-------
struct ConnectionView: View {
var numberOfDevice: Int
**var text: String = (BluetoothManager.peripheralArray[numberOfDevice]?.name)!**
// 'name' is a String property of the B.M. class's array's 'numberOfDevice index.'
var body: some View {
ZStack{
RoundedRectangle(cornerRadius: 10.0).fill(Color.blue)
Text(text).foregroundColor(Color.black)
}
}
}
You can use read-only computed property with short-hand.
var text: String {
return (BluetoothManager.peripheralArray[numberOfDevice]?.name)!
}
The error you encountered means you can't use the numberOfDevice variable to instantiate another variable. However, you can use the number you pass to your init method.
Try the following:
struct ConnectionView: View {
var numberOfDevice: Int
var text: String
init(numberOfDevice: Int) {
self.numberOfDevice = numberOfDevice
self.text = (BluetoothManager.peripheralArray[numberOfDevice]?.name)!
}
...
}
Note: I don't recommend force-unwrapping (!). If possible try to provide a default value.
Also, BluetoothManager looks like a type and not like an instance of a class. Make sure you access the peripheralArray property on the valid object and not on the BluetoothManager type.
You can use lazy keyword for that:
lazy var text: String = (BluetoothManager.peripheralArray[numberOfDevice]?.name)!
What is lazy?
lazy means that it will postpone initialization until someone calls the variable and it will not possible if self is not initialized. So you will be sure self is ready before accessing that value.
Why?
When you call numberOfDevice, you are actually calling self.numberOfDevice, but swift is smart enough to let you not explicitly write self keyword.
The issue here is that self is not initialized yet when you are assigning a value to a variable.
So you need to make sure the variable is initialized BEFORE accessing self.

access methods from protocols to viewcontroller

I was just implementing object class to access methods and protocols. Following
import UIKit
import Foundation
import Alamofire
import SVProgressHUD
protocol ParseAPIProtocol:class {
func APIresponse(responseDict: NSDictionary)
}
class ParseAPI :NSObject{
// weak var mydelegate : MyProtocols?
weak var delegateAPI:ParseAPIProtocol?
class func ParseUrl(postparameters: [String: AnyObject]!, url:String, current:UIViewController) {
print(postparameters)
print(url)
SVProgressHUD.show()
Alamofire.request(url, method: .post, parameters: postparameters, encoding: URLEncoding.default, headers: nil).responseJSON
{
(response:DataResponse<Any>) in
switch(response.result)
{
case .success(_):
if response.result.value != nil
{
let jsonResult = (try! JSONSerialization.jsonObject(with: response.data!, options: JSONSerialization.ReadingOptions.mutableContainers)) as! NSDictionary
print(jsonResult)
}
break
case .failure(_):
SVProgressHUD.dismiss()
print(response.result.error as Any)
break
}
}
}
Whenever I got the jsonResult, I just want to call fun APIresponse to parse result dictionary to viewcontroller class.
I declared the method on viewcontroller and its delegate.
But when I tried to call protocol method, it doesn't give me access to the method. May be I am doing something wrong. Please look into my method.
Thanks!
The way I see it there are several issues, although it's hard to tell because you don't show the relevant code in your view controller
(how you set up the delegate, how you call your parse API).
First, you never call your delegate with the result. With your current setup this isn't even possible: you define ParseAPI to have a
delegate (instance) member but define ParseUrl as a class func. I guess this is what you meant when saying "it doesn't give me access to the method".
Try defining it as a member function and then, in the success case call the delegate:
self.delegateAPI?.APIresponse(responseDict: jsonResult)
In your view controller, instantiate a ParseAPI instance and keep it alive (for example by storing it in a strong instance member), set its delegate,
and call ParseUrl(...). This call will return pretty much immediately and later, when the request and response is handled, your delegate method will
be called.
class ViewController: UIViewController, ParseAPIProtocol
{
var parser: ParseAPI?
func somewhere()
{
self.parser = ParseAPI()
self.parser!.delegateAPI = self
self.parser!.ParseUrl(postparameters: [:], url: "...", current: self)
}
func APIresponse(responseDict: NSDictionary)
{
print("VC got response:", responseDict)
}
}
Second, the current parameter is never used, you can remove it. Either that or you can remove the whole delegate thing and replace the current parameter
with a callback, something like this:
func ParseUrl(postparameters: [String: AnyObject]!, url:String, finished: #escaping (_ response: NSDictionary?) -> Void)
{
...
// success case:
finished(jsonResult)
// failure case:
finished(nil)
}
and call it like this:
self.parser!.ParseUrl(postparameters: [:], url: "...", finished: { (response: NSDictionary?) in
// handle response
})
You can also define your callback to have an error parameter so you can act accordingly when one occurs.
And finally, while I don't know SVProgressHUD, you probably wanna call SVProgressHUD.dismiss() in the success case, too.

Swift3, is it correct that you have to box this weak variable?

I have four obscure questions about the usual process of boxing weak references.
To demonstrate the issues, here's a notification system, Notes.
You'd use it like this...
class SyncedCell: UITableViewCell, Notes {
override func layoutSubviews() {
...
listen(forNote: "soloCell")
}
func editing() {
say(note: "soloCell")
...
input.becomeFirstResponder()
}
func note() {
print("Hooray, I got a note..")
editingCancel()
}
So, the code for Notes is below.
For a given key (say "soloCell") you simply keep an array of references to any object which wants to get a message when that key is called.
Naturally, these have to be weak references.
So, when a new object arrives that needs to be memorized in the list...
var b:_Box = _Box()
b.p = self
_notes[k]?.append(b)
(The "listen" function just adds that object to the list of items for that key. The "say" function runs through the list of listening items for that key: for each item - if the item has not gone away in the meantime - it sends a message.)
So! As far as I know, you cannot keep an array of weak references.
You have to box them up, as you see in the kodes below.
Really, is that correct? Using a box is ugly, is there a way to just plain keep a list of weak references? Without having to box?
Relatedly: in the kodes AnyObject is the base. Is that best?
Notice the protocol is not : class. This is disturbing and I'm not sure if it should be.
Note that very unfortunately in Swift - as far as I know - you cannot observe a weak reference going to nil. Is this still the case as of 2017? Is there any way at all to achieve this?
Footnote - regarding point 4, "is there any way to achieve this?" The only possibility seems to be to add an associatedPbject to the watched items. (Example of that)[https://stackoverflow.com/a/32607010/294884]
code...
struct _Box {
weak var p: AnyObject?
// note: I prefer to spell out the assigment,
// rather than have a convenience initializer here
}
var _notes:[String:[_Box]] = [:]
protocol Notes {
func note()
}
extension Notes where Self:AnyObject {
func listen(forNote k: String) {
if _notes.index(forKey: k) == nil {
_notes[k] = []
}
var b:_Box = _Box()
b.p = self
_notes[k]?.append(b)
}
func say(note k:String) {
if let _n = _notes[k] {
var k:Int = 0
print("notes.4 saying......")
for b in _n {
let p = b.p
if (p == nil) {
print("\(k) notes.4 there's one that's been removed")
}
else {
print("\(k) notes.4 sending ok...")
(p as! Notes).note()
}
k = k + 1
}
}
__noteCleaner()
}
func __noteCleaner() {
for var (k, _n) in _notes {
let kn = _n.count
for i in (0..<kn).reversed() {
let p = _n[i].p
if (p == nil) {
_n.remove(at: i)
let newk = _n.count
print("notes.4, removed a dud listener for key \(k) new length is \(newk)")
}
}
if (_n.count == 0) {
print("notes.4, removed a seemingly unused key \(k)")
_notes.removeValue(forKey: k)
}
}
}
}
I will concentrate on the heart of the problem.
You are trying to implement observer (listener) pattern by registering the observers on the subject. I guess the subject will then manually call every observer.
There is a simpler way to implement this. You can use a singleton. The observers will register at the singleton and the subject will notify the singleton when something important is happening.
There is special class for this, NotificationCenter. And it will keep only unowned references.
Delegates are not made for one observer, not for multiple observers.
Of course, there is a simple way to implement an array of weak references by wrapping the reference into a struct/object, see How do I declare an array of weak references in Swift? . I guess that's what your Box is doing?
However, I somehow think that your problem is caused by your architecture. You are mixing your view classes with your model classes and that's not a good idea.
To answer specifically your questions:
No. I believe the discussion about this in the Swift mailing list ended with "you can wrap it". You don't have to wrap it so simply. You can write your own version of array that will do the wrapping internally.
Use a generic instead of AnyObject, see the linked question above.
Yes this is disturbing. This is possible because you actually only use it on AnyObject (that is, classes) and you are removing the types in Box. With Box.P declared as Note this wouldn't work unless Note were a class protocol.
This is correct. See Know when a weak var becomes nil in Swift?. A simple workaround is to remove the listener manually when the listener is being deallocated (in deinit).