Modifying struct instance using call to name in function parameter - swift

I am attempting to use Parse to call up some variables and put them into a struct that is already initialized. The calling of the variables is happening smoothly and the data is available, but the inputing of the class into the function is not happening.
'unit' is a struct that has the name, hp, attack, etc. variables contained within it.
Is it not possible to pass along an instance of a struct and modify it's values like this? It would save me a lot of copy-pasting code to do it this way.
Thanks for your help!
func fetchStats(name: String, inout nameOfClass: unit) {
var unitStatArray = []
let query = PFQuery(className: "UnitStats")
query.whereKey("name", equalTo: name)
query.findObjectsInBackgroundWithBlock{(objects:[PFObject]?, error: NSError?)->Void in
if (error == nil && objects != nil){ unitStatArray = objects! }
nameOfClass.name = "\(unitStatArray[0].objectForKey("name")!)"
print("class name is \(nameOfClass.name)")
print("cannon name is \(cannon.name)")
nameOfClass.hitPoints = unitStatArray[0].objectForKey("hitPoints") as! Double
nameOfClass.hitPointsMax = unitStatArray[0].objectForKey("hitPointsMax") as! Double
nameOfClass.attack = unitStatArray[0].objectForKey("attack") as! Double
nameOfClass.defense = unitStatArray[0].objectForKey("defense") as! Double
nameOfClass.rangedAttack = unitStatArray[0].objectForKey("rangedAttack") as! Double
nameOfClass.rangedDefense = unitStatArray[0].objectForKey("rangedDefense") as! Double
nameOfClass.cost = unitStatArray[0].objectForKey("cost") as! Int
}
}
fetchStats("3-inch Ordnance Rifle", nameOfClass: &cannon)

This is an attempt to explain what I had in mind when writing my comment above.
Because there's an asynchronous call to findObjectsInBackgroundWithBlock, the inout won't help you here. The idea is to add a callback fetched like this:
func fetchStats(name: String, var nameOfClass: unit, fetched: unit -> ()) {
// your code as above
query.findObjectsInBackgroundWithBlock {
// your code as above plus the following statement:
fetched(nameOfClass)
}
}
This can be called with
fetchStats("3-inch Ordnance Rifle", nameOfClass: cannon) { newNameOfClass in
nameOfClass = newNameOfClass
}
(all of this code has not been tested)
The point is that you understand that your code is asynchronous (I know, I'm repeating myself). After you have called fetchStats you don't know when the callback (here: the assignment nameOfClass = newNameOfClass) will be executed. You cannot assume the assignment has been done after fetchStats has returned.
So whatever you need to do with the changed nameOfClass: the corresponding statements must go into the callback:
fetchStats("3-inch Ordnance Rifle", nameOfClass: cannon) { newNameOfClass in
// do whatever you want with the received newNameOfClass
}
Hope this helps.

Related

Trouble creating a more complex closure

I am trying to get my head around creating closures. I get the simpler ones like:
let squaredNumber = { (num: Int) -> (Int) in
return num * num
}
print( squaredNumber(9) ) // 81
and I understand how the sorting one works:
let team = ["Bob", "Rick", "Peter"]
print( team.sorted() ) // ["Bob", "Peter", "Rick"]
print( team.sorted(by: { $0 > $1 }) ) //["Rick", "Peter", "Bob"]
So now I am trying to create my own version of sorting one, in terms of setup.
In the sorting one, there is a plain function sorted() and a sorted(by:...) option.
The code I am playing with so far is (commented code is just to play with):
struct Person {
let name_given = { (fn: String?, ln: String?) -> String in
// return a different value if a nil is supplied
guard let fn1 = fn else { return "No First Name" }
guard let ln1 = ln else { return "No Last Name" }
// Only returns value if not nil
// if let fn1 = fn { return fn1 }
// if let ln1 = ln { return ln1 }
// returns what has been decided above
return "\(fn!) \(ln!)"
}
let no_name = {() -> String in
return "Hello"
}
}
let dad = Person()
print( dad.name_given("Jeff", "Smith") )
print( dad.no_name() )
but I can only get this to work dad.something() but I would like the option of a parameter closure in a function that is I am guessing an optional.
Am I even on the right track in terms of thinking this through.
Basically I want to create a function that execute different code based on wether they have a parameter or not.
So something like dad() or dad(name: {"Jeff", "Smith"}) or dad(age: {35})
The closures would combine strings or do something with the code and then return the result or something.
There is no story to the code above, I just want to understand how to create it.
/////////////////
Edit for clarity:
So I realise my aim was explained with a lot of confusion, because I am trying to figure this out.
Here is hopefully a better attempt:
So this code still doesn't work, but bare with me.
Take for example this:
static func closure(both: (_ a: String, _ b: String) -> String { a, b in
return "\(a) \(b)"
})
and then this:
static func closure(single: (_ a: String) -> String { a in
return "\(a)"
})
So then I would effectively be able to do something like this:
Person.closure(both: {"First", "Last"}) -> This would output "First Last"
and
Person.closure(single: {"First"}) -> This would output "First"
My outcome would be that I could have a static class that has a bunch of closures, but that are grouped.
So if I want to a bunch of string type closures, it would be easy to find them because you could do something like:
StaticStruct.string(<thing1>: {<closure params>})
StaticStruct.string(<thing2>: {<closure params>})
StaticStruct.string(<thing3>: {<closure params>})
or if I want to do something with numbers, it would be:
StaticStruct.numbers(<thing1>: {<closure params>})
StaticStruct.numbers(<thing2>: {<closure params>})
StaticStruct.numbers(<thing3>: {<closure params>})
I hope this makes more sense.
I like the way it looks when you do an array sort, that is why I started thinking like this.
What you are asking is flawed for Swift:
If you don't provide anything at all, not even nil, how can Swift know what's missing?
Think about this example:
print( dad.name_given("Connor") )
How can Swift know if you provided only the first name, or only the last name? In your example, you will need to be specific on what is not provided:
print( dad.name_given(nil, "Connor") ) // Surely only last name is provided
A function would solve this problem by providing a default value to the parameters, so you don't have to pass nil, but then you would need to specifically tell what you're passing, or Swift will assume:
func person(fn: String? = "", ln: String? = "") -> String {...}
print( dad.person(ln: "Connor")) // Surely passing the second parameter
func person(_ fn: String? = "", _ ln: String? = "") -> String {...}
print( dad.person("Connor")) // It will assume that you are passing the first parameter
But with closures, you can't provide a default value to parameters:
let name_given = { (fn: String? = "", ln: String? = "") -> String in...}
// Error: Default arguments are not allowed in closures
It might not solve your problem, but you can create a function that provides default values to the parameters (no need to pass nil), and accepts a closure to treat those parameters.
func describe(fn: String = "", ln: String = "", _ action: #escaping (String?, String?)->String) -> String {
let first: String? = fn == "" ? nil : fn
let second: String? = ln == "" ? nil : ln
return action(first, second)
}
print( dad.describe(ln: "Connor", dad.name_given)) // No first Name

How to use NSSet created from Core Data

I have the following core data model:
where Person to Codes is a one-to-many relationship.
I have a function which returns a Person record and if the code person.codes returns an NSSet of all the codes associated with that Person. The issue that I am having is how to use the NSSet.
person.codes.allObjects.first returns this data:
<Codes: 0x60000213cb40> (entity: Codes; id: 0xb978dbf34ddb849 <x-coredata://A2B634E4-E136-48E1-B2C5-82B6B68FBE44/Codes/p1> ; data: {
code = 4LQ;
number = 1;
whosAccount = "0xb978dbf34ddb869 <x-coredata://A2B634E4-E136-48E1-B2C5-82B6B68FBE44/Person/p1>";
})
I thought if I made person.codes.allObjects.first of type Codes, I would be able to access the code and number elements but I get an error: error: value of type 'Any?' has no member 'number'
Also, how can I search this data set for a particular code or number.
I appreciate that this is proabably a simple question but have searched and read the documentation to no avail. I suspect that may base knowledge is not sufficient.
Update
I have a CoreDataHandler class which contains the following code:
class CoreDataHandler: NSObject {
//static let sharedInstance = CoreDataHandler()
private static func getContext() -> NSManagedObjectContext {
let appDelegate = NSApplication.shared.delegate as! AppDelegate
return appDelegate.persistentContainer.viewContext
}
static func fetchPerson() -> [Person]? {
let context = getContext()
do {
let persons: [Person] = try context.fetch(Person.fetchRequest())
return persons
} catch {
return nil
}
}
I can fetch a person using:
let row = personTableView.selectedRow
let person = CoreDataHandler.fetchPerson()?[row]
Core Data supports widely native Swift types.
Declare codes as Set<Codes> in the Person class.
It's much more convenient than typeless NSSet.
You get a strong type and you can apply all native functions like filter, sort, etc. without type cast.
let codes = person.codes as! Set<Code>
Once that is done you can access the properties. Searching can be done by filtering for instance
let filteredCodes = codes.filter({ $0.code == "XYZ" })
will return all objects that has the code "XYZ". Or to get only one you can use
let code = codes.first(where: {$0.id == 1})
which will return the first object that has id = 1
A simple example getting all Person objects that has a given code
func findWithCode(_ code: String) -> [Person] {
guard let persons = CoreDataHandler.fetchPerson() else {
return []
}
var result = [Person]()
for person in persons {
let codes = person.codes as! Set<Code>
if codes.contains(where: { $0.code == code }) {
result.append(person)
}
}
return persons
}

Allocating the results of Reverse Geocoding to a global variable

I am using Swift 4 and Xcode 9.3.1. I'm including screenshots of the code.
I am new to mobile development/ programming in general and have been thinking about how to phrase this. So this is the best I can explain it:
I am building an app that gets the user's location in order to send assistance through to them. The app gets the user's coordinates, and displays a TextView with their address information. So pretty straight forward mapKit/coreLocation functionality. So far, so good: Getting the coordinates with startUpdatingLocation() works fine, and I've used Reverse Geocoder to get the street name & locality. But they-- meaning the decoded street and locality strings-- only print out if I call them within the closure, not outside it. I've understood (correctly or incorrectly?) that variables that need to be available for multiple functions within a class should to be declared globally at the top. However I can't figure out how to extract the information from this closure in order to use it elsewhere.
I've been googling and reading through questions in stackOverflow and I feel like something really simple is missing but can't figure out what. Things I've tried unsuccessfully so far:
1_ Defining global variables as empty strings at the beginning of the class
and using the variable names inside the closure where the geocoding reverse method happens, in an attempt to store the resulting strings, but when I try to print the variables outside the closure, the global variable is still and empty string ("").
[global variables declared at the top][1]
2_Defining an empty, global array of strings and appending the information from inside the closure to the global array. Still got an empty array outside the closure. (so same as 1)
3_Create a function --func decodedString()-- to return the data as a String, so I can use it by declaring
*let streetLocation : String = decodedString()*
However when I declare that function like this :
var street = ""
var locality = ""
// Get the street address from the coordinates
func deocodedString() -> String {
let geocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(location) { placemarks, error in
if let placemark = placemarks?.first {
self.street = placemark.name!
self.locality = placemark.locality!
let string = "\(self.street), \(self.locality)"
return string
}
}
}
I get an error of: Unexpected non-void return value in void function
unexpected no void return value in void function
Lastly, if I pass the information straight into a TextView within the closure by using the code below, my textView updates successfully-- but I can't format the strings, which I need to do in order to make them look like the design instructions I'm following (aka some bold text, some regular text, and some different sizes of text):
CLGeocoder().reverseGeocodeLocation(location) { placemarks, error in
if let placemark = placemarks?.first {
self.street = placemark.name!
self.locality = placemark.locality!
let string = "\(self.street), \(self.locality)"
self.addressTextView.text = string
}
}
So that's why I can't just pass it through with the textView.text = string approach.
I'd appreciate some help...I have been looking though StackOverFlow, youtube and other tutorial places but I can't figure out what I'm missing, or why my function declaration generates an error. I have already destroyed and reversed my code several times over last 24 hs without getting an independent string that I can apply formatting to before passing it into the textView and I'm at a loss as to how else to approach it.
When you call this function the reverseGeocodeLocation runs in the background thread. So if you want to return the address in this method you should use escaping closure.
func getaddress(_ position:CLLocationCoordinate2D,completion:#escaping (String)->()) {
let geocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(location) { placemarks, error in
if let placemark = placemarks?.first {
let street = placemark.name!
let locality = placemark.locality!
let string = "\(street), \(locality)"
completion(string)
}
}
}
self.getaddress(position.target) { address in
print(address)
self.addressTextView.text = address
}
I had a problem with google geocoder to update the label on the map screen.
So I did this, first, create
swift file name: GoogleAPI just call it as you like.
class GoogleAPI {
static let sharedInstance = GoogleAPI()
private init() { }
var addressValue = ""
public func geocoding(lat: Double, long: Double) {
Alamofire.request("https://maps.googleapis.com/maps/api/geocode/json?latlng=\(lat),\(long)&key=YOUR_GOOGLE_KEY").responseJSON { (response) in
if response.result.isSuccess {
let dataJSON : JSON = JSON(response.result.value!)
self.geocoding(json: dataJSON)
} else {
print("Error \(response.result.error!)")
}
}
}
fileprivate func geocoding(json: JSON) {
let json = json["results"]
let address = json[1]["formatted_address"].stringValue
addressValue = address
print("pin address \(addressValue)")
}
}
This is an API call to Google to fetch all from a response and parse the only street.
After that go to your View Controller with a map where is the pin, map etc..
Set up a pin, marker to be draggable. [marker1.isDraggable = true]
Then add this function
mapView(_ mapView: GMSMapView, didEndDragging marker: GMSMarker)
and add call from above like this :
func mapView(_ mapView: GMSMapView, didEndDragging marker: GMSMarker) {
GoogleAPI.sharedInstance.geocoding(lat: marker.position.latitude, long: marker.position.longitude)
DispatchQueue.main.async {
self.txtSearch.text = GoogleAPI.sharedInstance.addressValue
}
}
txtSearch is my search text field.
yea I know, that can be done better, but no time. this is working.
Swift 4.2

Why variable doesn't change it's value when used in a Parse query inside a loop

My problem is as follows: I have an argument class where all arguments have a parentId which are indeed objectId of other arguments. I would like to write a query where I can get a list of all arguments that are connected to each other with this kind of parent-child relationship. So i have tried this..
class ArgumentViewController: UIViewController {
var all = [String]()
var temporaryId = "vEKV1xCO09"
override func viewDidLoad() {
super.viewDidLoad()
for _ in 1...3 {
let query = PFQuery(className: "Argument").whereKey("objectId", equalTo: temporaryId)
query.findObjectsInBackground { (objects, error) in
if let arguments = objects {
for argument in arguments {
self.all.append(argument["parentId"] as! String)
print(self.all)
self.temporaryId = argument["parentId"] as! String
}
}
}
}
}
but the problem is temporaryId inside the loop does not update itself. It remains the same in all iterartions. Hence when i do print(self.all) i simply get an array of 3 strings which all are parent of my initial argument
My goal is to get an array that is [parent of my initial argument, parent of parent of my initial argument, parent of parent of parent of my ...]
I have searched similar topics but couldn't find a solution. Any help would be very appreciated.
Since query.findObjectsInBackground runs in background thread, there is no way to update temporaryId with the newly retrieved #"parentId" in every for loop.
So I guess you could create a recursive function, something like:
func getParentId() {
let query = PFQuery(className: "Argument").whereKey("objectId", equalTo: temporaryId)
query.findObjectsInBackground { (objects, error) in
if let arguments = objects {
for argument in arguments {
self.all.append(argument["parentId"] as! String)
print(self.all)
self.temporaryId = argument["parentId"] as! String
while (all.count <= 3) {
getParentId()
}
}
}
}
}
I haven't done enough practice in Swift as i'm an Objective-C developer, so I'm sorry if I made some syntax errors.

Objects in Swift: Value of 'Object' has no member

Here's my doozy.
I've got this lovely little function in a file called functions.swift
//functions.swift
func latestActiveGoal() -> Object {
let realm = try! Realm()
let currentGoal = realm.objects(Goal).filter("Active == 1").sorted("CreatedOn").last
return currentGoal!
}
which returns a Goal object. (A Goal might be wanting to lose weight, or stop being so inept at Swift).
In a different view controller, I want to access this object. Here's what I'm trying:
//viewController.swift
#IBOutlet weak var aimText: UILabel!
let funky = functions()
func getGoals(){
var currentGoal = funky.latestActiveGoal()
print(currentGoal)
aimText.text = currentGoal.Title
}
The print(CurrentGoal) output shows this:
Goal {
id = 276;
Title = Goal Title;
Aim = Aim;
Action = Nothing;
Active = 1;
CreatedOn = 2016-02-12 00:14:45 +0000;
}
aimText.text = currentGoal.Title and aimText = currentGoal.Title both throw the error:
Value of 'Object' has no member 'Title'
By printing the contents of the object, I can see the data, but can't figure out how. Any help greatly appreciated.
As the error message said, currentGoal is a value of Object type which doesn't have member Title.
This is because function latestActiveGoal returns Object instead of Goal. You just need to make it return Goal by change the return type:
func latestActiveGoal() -> Goal {
Just replace your functions with below code.
It will works perfect.
This fuction will check if goal available, then only it will return.
func latestActiveGoal() -> Object? {
let realm = try! Realm()
let currentGoals = realm.objects(Goal).filter("Active == 1").sorted("CreatedOn")
if currentGoals.count > 0 {
return currentGoals.last;
}
return nil;
}
Your getGoals method will be as follow.
func getGoals(){
if let currentGoalObject = funky.latestActiveGoal() {
print(currentGoalObject)
let goal = currentGoalObject as! Goal
print(goal.Title)
aimText.text = goal.Title
}
}