Trying to figure out CompletionHandler in Swift? - ios

So I'm trying to do some programming with Phillips HUE lights and I've already made a bridging header and imported the necessary info to use the Hue SDK with Swift, but all of the guidance is written for Objective-C. I'm having trouble understanding/declaring this function in Swift:
// Start search for bridges
[self.bridgeSearch startSearchWithCompletionHandler:^(NSDictionary *bridgesFound) {
// Search is complete, handle results (dictionary contains IP and mac addresses of bridges found)
[self showBridgesFound:bridgesFound];
}
When I go to call this in Swift, I type bridgeSearch.startSearch() and Xcode automatically adds this with a Completion handler
//Search for bridges
let bridgeSearch: PHBridgeSearching = PHBridgeSearching()
bridgeSearch.startSearch { ([AnyHashable : Any]?) in
//code
}
I know how to get the information (found IP addresses) stored into an NSDictionary in Obj-C, but I'm not sure what to do here in terms of storing the IP addresses into some sort of data structure.
Thanks!

The problem is merely that you have not given the incoming parameter any name. Give it one:
bridgeSearch.startSearch { bridgesFound in
// code involving bridgesFound
}

Related

Swift: Using parsed JSON Data outside of a closure [duplicate]

This question already has answers here:
Returning data from async call in Swift function
(13 answers)
Closed last year.
I am building a mobile app with swift, and am having some syntax issues as I am not a developer. The structure and logic of the application is really rough and surely incorrect, however we just need something that functions. (It is a school project and my team got no devs).
Anyways, we have a MySQL database that will be used as a middleman between our badge server/admin app, and our mobile app. Currently when you go to https://gatekeeperapp.org/service.php , you will see the current database data, taken by a php script and hosted there as JSON. Currently in Swift I have a struct with a function that takes this JSON data, and maps it to variables. The idea is to then pass these pulled variables into a separate set of functions that will check the pulled long/lat against the mobile devices location, and then return whether they match or not. This value would be updated, re-encoded to JSON, and pushed to a web service that would go about changing the values in the database so the badge server could use them.
Where I am currently I can see that values are being pulled and mapped and I can set a variable in a separate function to the pulled value, but then I can only seem to output this value internally, rather than actually use it in the function. I get a type error saying that the pulled values are of type (). How can I properly use these values? Ultimately I think I would want to convert the () to a double, so I could properly compare it to the Long/Lat of the device, and then will need to re-encode the new values to JSON.
Swift Code -- struct function
Swift code -- JSON struct
Swift code -- using pulled data
Your closure is called asynchronously, which means that the outer function where you are expecting to use the values has already returned by the time the closure is called. Instead, you probably need to call some other function from the closure, passing the values you've received.
class MyClass {
func fetchUserData() {
UserData().fetchUser { [weak self] user, error in
DispatchQueue.main.async {
if let user = user {
self?.handleSuccess(userID: user)
} else if let error = error {
self?.handleError(error)
}
}
}
}
private func handleSuccess(userID: String) {
print(userID)
// Do something with userID. Maybe assign it to a property on the class?
}
private func handleError(_ error: Error) {
print(error)
// Handle the error. Maybe show an alert?
}
}

Manual logged screen_view events sometimes have screen_name - (not set)

My project consist of obj-c and swift classes. I use Firebase 7.3.0.
I manually log screen_view event for my screens. I call this method in viewWillAppear or viewDidAppear like this:
#objc class MyAnalyticConstants: NSObject {
static let myScreenName = "AwesomeScreen"
}
class MyViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
MyAnalyticsClass().logScreenViewEvent(name: MyAnalyticConstants.myScreenName)
}
}
class MyAnalyticsClass {
func logScreenViewEvent(name: String, parameters: [String: Any]? = nil) {
var param = [String: Any]()
if let parameters = parameters {
param = parameters
}
param[AnalyticsParameterScreenName] = name
logEvent(name: AnalyticsEventScreenView, parameters: param)
}
func logEvent(name: String, parameters: [String: Any]?) {
analytics.logEvent(name, parameters: parameters)
}
}
I turned off automatic screenview reporting by setting FirebaseAutomaticScreenReportingEnabled to NO (Boolean) in the Info.plist. I use struct with static names for my screens.
However, sometimes I see (not set) value for "screen_view" event inside my google analytics path exploration for production. I can't catch this while using DebugView.
screenshot
I would really appreciate it, if somebody could help me to fix or explain it.
EDIT:
I swizzled firebase method
+ (void)xxx_logEventWithName:(NSString *)name
parameters:(nullable NSDictionary<NSString *, id> *)parameters {
[self xxx_logEventWithName:name parameters:parameters];
if ([name isEqualToString:#"screen_view"] && [parameters[#"screen_name"] length] <= 3) {
NSLog(#"%#", #[][1]);
}
and jumped through app during 30 min. I didn't catch up crash. Any other ideas?
Ok, there are a few things you could typically do in this case.
I wrote it as an afterthought, but it's something you should make sure of before doing technical debugging that follows: you should go to your analytics property/view and debug the filters. Maybe you have replacing filters interfering with your values, but I presume you checked your data in a full and unfiltered view where your app is the sole "stream" of data. This is important. The bug can come from a different app to this property, so either make sure you're the only source, or make sure you filter out other sources/apps/platforms.
Check your logEvent function calls where you either send AnalyticsEventScreenView or just the "screen_view" string as the first parameter. You see how the Firebase lib uses one function to send all kinds of events? They now treat screenviews as events. Which has its elegancy, but also may lead to unintended mistakes. Check what the globals actually mean in here: https://github.com/firebase/firebase-cpp-sdk/blob/0c8c8b29bc2d62d66c6ac49ff2c3fb04f815a687/analytics/ios_headers/FIREventNames.h
Check your logScreenViewEvent function calls. Pay attention to cases when you pass the first parameter as a variable. Also make sure you're never setting the AnalyticsParameterScreenName, which is also known as a string "screen_name" from here: https://github.com/firebase/firebase-cpp-sdk/blob/0c8c8b29bc2d62d66c6ac49ff2c3fb04f815a687/analytics/ios_headers/FIRParameterNames.h in the parameters dictionary, the second attribute. Cuz setting it there will effectively overwrite whatever is the first attribute you're setting. I actually usually suggest having only one argument for the screenview function declaration, especially to avoid collisions like this.
Oh, almost forgot. Make sure you ALWAYS use your neat MyAnalyticsClass wrapper and never call the native logEvent(). I would just check all files where you include the Firebase sdk and see if it should be replaces with the wrapper.
Finally, if the above didn't help, you can insert the check in both your function wrappers to throw an error whenever the event name equals to "screen_view" and the "screen_name" parameter's length not more than 2 character (I'm just trying to include all falsy values, so null, undefined, nil, whatever). And run unit tests or even better - regression testing with things set like that. Well, or manually test it out, watching for the errors in the console rather than the web debugger.

How to get full list of address components with Google Maps iOS SDK

Good morning SO!
I am currently working on an iOS app that requires me to use Google Maps' reverse geocoding features. The tricky part is that I need to be able to access the full list of address components (i.e. the full "address_components" value from the JSON returned from this example, including short and long names)
When using the Google Maps iOS SDK, it seems that all results from reverse geocoding are given in the form of GMSAddress, which only exposes a small subset of these values (and no short versions). I have so far not found a way to access what I need.
Is there any way to get these values through the iOS SDK? If not, is it okay to try and call the google maps API directly? Has anybody tried?
Thank you for your time,
Julien P.
I am working on one such app...I don't know about iOS SDK.
But, Google API will help for this. This tutorial here gets the address of a location you click on:
https://www.raywenderlich.com/109888/google-maps-ios-sdk-tutorial
You will not be able to call Google API directly. You will need to add a server for this. This tutorial gives a detailed and clear way to do this.
Hope this helps. All the best
I found an easy solution using the LMGeocoder library for iOS.
Inside LMAddress, there is a lines attribute that will contain all the information. You can access it easily with the following piece of code.
func getAddressComponent(name: String, lines: [[NSObject: AnyObject?]]) -> (short: String?, long: String?)? {
let filteredLines = lines.filter { ($0["types"] as! [String]).contains(name)
guard let line = filteredLines.first else {
return nil
}
let longName = line["long_name"] as? String
let shortName = line["short_name"] as? String
return (short: shortName, long: longName)
}
This is using Swift 2.3 but will probably work for Swift 3 as well. Hope it helps anyone! :)
Julien

Kinvey 3.3.5 SDK Query By Ids

I am currently going through the processes of migrating swift 2.3 to 3 using the most updated Kinvey SDK (version 3.3.5). They have done a ton of updates since the 1x versions. My question is has anyone successfully been able to query on the PersistableKeyID field and pull multiple objects?
I use to be able to use the "loadObjects" function which would take an array of strings as an argument. This function has since been depreciated and replaced with find(byId). See below:
dataStore.find(byId: "only takes one") { uClass, error in
if let uClass = uClass {
//succeed
print("UClass: \(uClass)")
} else {
//fail
}
The issue is, it will only take a single string as an argument. I have attempted to use the query functionality, but I cannot get it to take the "_id" field as a parameter. Using the following code:
//Just statically creating the sectionID array for now. This will dynamically be created
testIDs = ["58668307206c11177e5ab0d4", "58668307206c11177e5ab0d4", "57ad00a505a2bb55632659c3"]
let sectionStore = DataStore<Section>.collection()
let sectionQuery = Query(format: "_id IN %#", testIDs)
sectionStore.find(sectionQuery) {sectionResult, error in
if let sectionResult = sectionResult {
self.sectionsTest = sectionResult
self.sectionCollectionView.reloadData()
} else{
//Error
}
}
I receive the error:
'Invalid property name', reason: 'Property '_id' not found in object of type 'Section'
Anyone have an idea on how to perform this now that "loadObjects" has been depreciated? There is no delivered "find(byIds)" that I could find.
Jbone107,
I was able to get results with this, let me know if the below works for you.
let id:[String] = ["5855026650a816ec29012908","5855024a21400c5b492bea20"]
let query = Query(format: "_id IN %#", id)
dataStore.find(query) { data, error in
if let data = data {
//succeed
print(“Data: \(data)")
} else {
//fail
print("fetching failed")
}
}
Thanks,
Pranav,
Kinvey
Answered: Per the Data Store Guide for iOS, by default the ".collection()" is of type "cache". The "Cache" type will store data locally. This must be why "Realm" is now included with the version 3x SDK.
I updated my DataStore collection to:
let sectionStore = DataStore<Section>.collection(.network)
I added ".network" to force the query to pull from the backend rather than the cache file. This actually identified "_id" as a property and the query worked successfully. For some reason the "cache" file isn't storing this as a property.
Additional SDK Question Answered
I was having an issue pulling NSNumber from the Kinvey backend. This ended up being a similar issue related to the "cache" query. I reviewed the Realm support site as a last resort effort to try and figure this out. I found that Realm doesn't actually support type "NSNumber".
Excerpt taken from: https://realm.io/docs/swift/latest/
Realm supports the following property types: Bool, Int8, Int16, Int32, Int64, Double, Float, String, NSDate, and NSData.
Unfortunately, Kinvey doesn't support "Int" types. As a work around, I have changed them to string and am just converting back to "Double" or another type after I pull the data. However, if I just use ".network" collection types, then NSNumber still works.
Thanks,
James

How to read associative array sent from cloud code in iOS sdk?

I have a cloud code function on Parse.com and I receive following response, can anybody please tell me how to read it in iOS code ?
{"result":[{"week":1,"avg":50.5},{"week":2,"avg":0},{"week":3,"avg":0},{"week":4,"avg":29},{"week":5,"avg":0},{"week":6,"avg":0},{"week":7,"avg":0}]}
Thanks.
Reply from cloudcode are already decoded into NSDictionary / NSArray compatible object or possibly mapped subclasses of PFObject if you have registered them.
Do NSLog(#"reply from parse : %#",reply); in cloud function block to see that data are already parsed.
You should be then able to process data like
for (NSDictionary * currentWeek in reply) {
....
}

Resources