save promise fulfill and reject promisekit - ios

Hi I'm trying to create a promise and then save the function fulfill and reject into in array or dictionary . I don't know if this is possible to do I get some compiler erros. I know you can store functions inside array but I think since is inside the promise I need to do something else, here is my code
let requestPromise = Promise<Bool> { fulfill, reject in
self.socket.emit(message,dic)
let dicFunc = [ "fulfill": fulfill, "reject":reject]
self.request.updateValue(dicFunc, forKey: uuid)
}
I get error
Cannot invoke 'updateValue' with an argument list of type '([String : (NSError) -> Void], forKey: String)'

The fulfill and reject variables have different types so they can't both be contained as separate values within the same dictionary or array. (Unless possibly if you make a dictionary/array that contains Any but that will loose needed type info.)
Check out Promise.pendingPromise(). You can hold an array of PendingPromises.

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?
}
}

Object (Optional Any): get elements

I'm trying to pass information with an observed notification in my app.
The standard way to do that, is to set the userinfo. However, the data I want to pass is a Set, not a dictionary.
So, I do this:
NotificationCenter.default.post(name: MY_NOTIFICATION_NAME, object:self.productIds)
The object arrives fine, but now I'm unable to get to it:
in the console I do this:
po notification.object!
▿ 2 elements
- 0 : ZTJ
- 1 : ZTM
However, when I try to get to one of the elements, I get this:
po notification.object![0]
error: <EXPR>:8:21: error: value of type 'Any' has no subscripts
notification.object![0]
What am I doing wrong?
You know that notification.object is a Set, but the compiler doesn't because it's declared as Any, which means it could be anything, and so it can't find which implementation of object[0] it should use.
To read this object you need to cast it to a set.
if let mySet = notification.object as? Set<MyType> {
// Do whatever
}
Keep in mind that the object property of Notification is designed to be used as a filter, If you pass a value when adding an observer, you'll only get notifications that are sent with that exact same object.
The userInfo dictionary is to send related information, like your set. In this case I would send a dictionary like this:
NotificationCenter.default.post(name: MY_NOTIFICATION_NAME, object: nil, userInfo: ["products": productIds])
The notification has an object type of Any?.
When you're poing it in the console, you're asking it to print its description, which Any can do.
When you're asking it to subscript, Any can't do that, because subscripting is not defined on that type. You need to cast it to the expected type:
po (notification.object as? [String])?[0]
In general, it's best to nail down the type of any Any as soon as you can. Think of Any as a box used to send things through the post. The first thing you do is open it and find out what's inside.

Determining Swift Types That Can Be Stored in UserDefaults

I am in the beginning stages of developing an open-source utility for storing state in the Bundle UserDefaults.
I'm encountering an issue when I add non-Codable data types to my Dictionary of [String: Any].
I need to be able to vet the data before trying to submit it, because the UserDefaults.set(_:) method won't throw any errors. It just crashes.
So I want to make sure that the Dictionary that I'm submitting is kosher.
I can't just check if it's Codable, because it can sometimes say that it isn't, when the struct is actually good. (It's a Dictionary<String, Any>, and I can cram all kinds of things in there).
I need to validate that the Dictionary can produce a plist. If this were ObjC, I might use one of the NSPropertyListSerialization methods to test the Dictionary, but it appears as if this set of methods is not available to Swift.
According to the UserDefaults docs, there are a specific set of types and classes that are "plist-studly."
I think testing each type in the list is unacceptable. I need to see if I can find a way to test that won't be screwed the first time Apple updates an OS.
Is there a good way to test a Dictionary<String, Any> to see if it will make UserDefaults.set(_:) puke?
The Property List type set of UserDefaults is very limited. The supported types are
NSString → Swift String
NSNumber → Swift Int, Double or Bool
NSDate → Swift Date
NSData → Swift Data
Arrays and dictionaries containing the 4 value types.
Any is not supported unless it represents one of the 4 value or 2 collection types.
Property List compliant collection types can be written to UserDefaults with PropertyListSerialization (even in Swift).
There are two protocols to serialize custom types to Data
Codable can serialize structs and classes.
NSCoding can serialize subclasses of NSObject.
All types in the structs/classes must be encodable and decodable (means conform to the protocol themselves).
The APIs of PropertyListSerialization / PropertyListEncoder/-Decoder and NSKeyed(Un)Archiver provide robust error handling to avoid crashes.
UPDATE[1]: And, just because I like to share, here's the actual completed project (MIT License, as is most of my stuff)
UPDATE: This is the solution I came up with. Even though I greenchecked vadian's excellent answer, I decided to get a bit more picky.
Thanks to matt pointing out that I was looking under the wrong sofa cushions for the keys, I found the Swift variant of NSPropertyListSerialization, and I use that to vet the top level of the tree. I suspect that I'll need to refactor it into a recursive crawler before I'm done, but this works for now.
Here's the code for the _save() method at the time of this writing. It works:
/* ################################################################## */
/**
This is a private method that saves the current contents of the _values Dictionary to persistent storage, keyed by the value of the "key" property.
- throws: An error, if the values are not all codable.
*/
private func _save() throws {
#if DEBUG
print("Saving Prefs: \(String(describing: _values))")
#endif
// What we do here, is "scrub" the values of anything that was added against what is expected.
var temporaryDict: [String: Any] = [:]
keys.forEach {
temporaryDict[$0] = _values[$0]
}
_values = temporaryDict
if PropertyListSerialization.propertyList(_values, isValidFor: .xml) {
UserDefaults.standard.set(_values, forKey: key)
} else {
#if DEBUG
print("Attempt to set non-codable values!")
#endif
// What we do here, is look through our values list, and record the keys of the elements that are not considered Codable. We return those in the error that we throw.
var valueElementList: [String] = []
_values.forEach {
if PropertyListSerialization.propertyList($0.value, isValidFor: .xml) {
#if DEBUG
print("\($0.key) is OK")
#endif
} else {
#if DEBUG
print("\($0.key) is not Codable")
#endif
valueElementList.append($0.key)
}
}
throw PrefsError.valuesNotCodable(invalidElements: valueElementList)
}
}

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

Casting generic array in swift cause fatal error

I have a class which acts as a BLL, wrapping a service protocol. The service protocol provides a list of SerializableObjectProtocol objects. For instance, I have User, which implements SerializedObjectProtocol.
The following function casts a SerializedObjectProtol array into a User
public func Get() -> [T]
{
let result = self._service.Get()
return result as! [T]
}
As a result, I am getting the following error:
array element cannot be bridged to Objective-C
I am aware that the code is error prone, because if the object is not T, down casting cannot happen. As a result, here is what I can verify:
T in constrained to implement SerializedObjectProtol i.e.
class DataLayer<T:SerializableObjectProtocol>
T is type User. The result is an array of user. i.e. [User]
I can get around this issue, but I have to manually cast each item. As a result, this works perfectly fine:
var returnArray = [T]()
for item in result
{
returnArray.append(item as! T)
}
return returnArray;
I have just picked up Swift for a project so I have limited experience with it. As a result, I have gone out to see if what I am trying is possible (casting array [S] to [T]). It seems that it is possible if the array is [Any].
Is this a valid operation in Swift? Or is casting this way not possible.
Generally it is not possible to cast directly between an array of Any to the type it contains, because Any has a completely different representation in memory: sizeof(Any) isn't equal to sizeof(User)! An array of 10 Anys might have a length of 320 bytes, but 10 Users only need 80 bytes, the same applies to any protocol. Conclusion: You need to cast every single item.
Maybe do it like this:
return results.map{ $0 as! User }
or if you're not sure whether every item is a User, you can only return the Users like this:
return results.flatMap{ $0 as? User }
If you're still having problems, please post some minimal code that still produces the error, it's really hard to understand what your code looks like without the actual code

Resources