Casting generic array in swift cause fatal error - ios

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

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

Accessing CFArray causes crash in Swift

My following code crashes with EXC_BAD_ACCESS, and I do not understand why. My initial understanding is that the memory retention in this case should be automatic, but it seems I am wrong... Maybe someone can help. Thank you! The code is written in Swift 5 and runs on iOS 15.2 in XCode 13.2.1.
Casting to NSArray causes trouble...
let someFont = CGFont("Symbol" as CFString)!
if let cfTags: CFArray = someFont.tableTags {
let nsTags = cfTags as NSArray
print(nsTags.count) // Prints: 16
let tag0 = nsTags[0] // CRASH: Thread 1: EXC_BAD_ACCESS (code=257, ...)
}
Alternatively, using CFArray-API causes also trouble (The crash message is about a misaligned pointer but the root cause seems also the bad access, which occurs e.g. if I replace UInt32.self by UInt8.self, and hence eliminate the alignment problem).
let someFont = CGFont("Symbol" as CFString)!
if let cfTags: CFArray = someFont.tableTags {
print(CFArrayGetCount(cfTags)) // Prints: 16
let tag0Ptr: UnsafeRawPointer = CFArrayGetValueAtIndex(cfTags, 0)!
tag0Ptr.load(as: UInt32.self)// CRASH :Thread 1: Fatal error: load from misaligned raw pointer
}
The issue here is that the CGFont API uses some advanced C-isms in their storage of table tags, which really doesn't translate to Swift: CGFontCopyTableTags() returns a CFArrayRef which doesn't actually contain objects, but integers. (This is technically allowed through CFArray's interface: it accepts void *s in C, into which you can technically stuff any integer which fits in a pointer, even if the pointer value is nonsense...) Swift expects CFArrays and NSArrays to only ever contain valid objects and pointers, and it treats the return values as such — this is why accessing via NSArray also fails (Swift expects an object but the value isn't an object, so it can't be accessed like a pointer, or retained, or any of the things that the runtime might expect to do).
Your second code snippet is closer to how you'll need to access the value: CFArrayGetValueAtIndex appears to return a pointer, but the value you're getting back isn't a real pointer — it's actually an integer stored in the array directly, masquerading as a pointer.
The equivalent to the Obj-C example from the CGFontCopyTableTags docs of
tag = (uint32_t)(uintptr_t)CFArrayGetValue(table, k);
would be
let tag = unsafeBitCast(CFArrayGetValueAtIndex(cfTags, 0), to: UInt.self)
(Note that you need to cast to UInt and not UInt32 because unsafeBitCast requires that the input value and the output type have the same alignment.)
In my simulator, I'm seeing a tag with a value of 1196643650 (0x47535542), which definitely isn't a valid pointer (but I don't otherwise have domain knowledge to validate whether this is the tag you're expecting).

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

save promise fulfill and reject promisekit

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.

iOS 8.1 Swift Dictionary Error: EXC_BAD_ACCESS (XCode 6.1)

TLDR: I'm getting an EXC_BAD_ACCESS error using swift in XCode 6.1 building for iOS8.1. I believe the issue is likely a compiler error.
I am making an app wherein the user is allowed to add (Korean) words from a dictionary (in the English sense) to a word list. I have the following classes that define a word (with associated definitions, audio files, user-interaction statistics, etc.) and a word list (with an associated list of words and methods to add/remove words to the list, alphabetize the list, etc):
class Word: NSObject, NSCoding, Printable {
var hangul = String("")
var definition = String("")
// ...
}
class WordList: NSObject, NSCoding {
var title = ""
var knownWords = Dictionary<String,String> ()
// ...
func addWordToList(wordName: String, wordDefinition: String) {
// Debug statements
println("I am WordList \"\(self.title)\" and have the following knownWords (\(self.knownWords.count) words total): ")
for (_wordName,_wordDefinition) in self.knownWords {
println("\t\"\(_wordName)\" : \(_wordDefinition)")
}
println("\nI am about to attempt to add the word \"\(wordName)\" with definition \"\(wordDefinition)\" to the above dictionary")
// Add word to wordList, including the 'let' fix
fix_EXC_BAD_ACCESS_bug()
knownWords[wordName] = wordDefinition // EXC_BAD_ACCESS
}
func fix_EXC_BAD_ACCESS_bug() {
// This empty line attempts to solve a exc_bad_access compiler bug when adding a new value to a WordList dictionary
let newDic = self.knownWords
}
// ...
}
Next I have a UITableViewController with a UISearchBar that I use to display the dictionary (again in the English sense) of words to the user. The user adds words by tapping a button (which is displaying an image) on the right of each cell, which calls the #IBAction func addWord() in the viewController:
class AddingWordsToWordList_TableViewController: UITableViewController, UISearchResultsUpdating {
var koreanDictionary = KoreanDictionary() // Custom class with method 'getWord(index: Int)' to return desired Word
var filteredSearch = [Word]()
// ...
// Add word
#IBAction func addWord(sender: UIButton) {
// Add word
if self.resultSearchController.active {
let word = filteredSearch[sender.tag]
wordList.addWordToList(word.hangul, definition: word.definition) // Enter addWordToList() here
} else {
let word = koreanDictionary.getWord(sender.tag)
wordList.addWordToList(word.hangul, definition: word.definition)
}
// Set image to gray
sender.imageView?.image = UIImage(named: "add133 gray")
// Save results
wordList.save()
// Reload table data
tableView.reloadData()
}
// ...
At first the app compiles and seems to run fine. I can add a new word list and start adding words to it, until I add the 8th word. I (always on the 8th word) get a EXC_BAD_ACCESS error in (WordList) addWordToList with the following println information:
I am WordList "School" and have the following knownWords (7 words total):
"방학" : school vacation
"대학원" : graduate school
"학년" : school year
"고등학생" : high school student
"초등학교" : elementary school
"학생" : student
"학교" : school
I am about to attempt to add the word "대학생" with definition "college student" to the above dictionary
Note that the order in which I add the words appears irrelevant (i.e. the word "college student" can be added successfully if it is one of the first 7 words). There is nowhere in the code where I explicitly change behavior based on the number of words in a word list (except to display the words in a word list as cells in a UITableView), or any dependencies that would (to my knowledge) make the 8th word a special number. And indeed this number can change by using the let hack= solution (see below) to a different number, but for a particular build is always the same number.
At this point I'm at a complete loss of what to do. I'm relatively new to swift and have spent quite a few hours looking up how to fix exc_bad_access errors. I've come to the following point:
It seems exc_bad_access errors usually mean one of three things (http://www.touch-code-magazine.com/how-to-debug-exc_bad_access/)
An object is not initialized
An object is already released
Something else that is not very likely to happen
I don't feel like either 1 or 2 can be the case for me, as right before we get the access error we can print out the contents of the dictionary in question (knownWords). However, as is always the case it is highly likely I'm missing something obvious, so please let me know if this is indeed true.
If the error is caused by 3 above ("something else") I have tried the following:
(From EXC_BAD_ACCESS on iOS 8.1 with Dictionary and EXC_BAD_ACCESS when updating Swift dictionary after using it for evaluate NSExpression solutions)
I have tried different variations of the let stupidHack = scenario, and it does sometimes change the results, but only the number of entries allowed before crashing (i.e. I can sometimes get to the ~15th/16th entry in the dictionary before the error). I have not been able to find an actual, bug-free solution using this method, however.
I have rebuilt using both Release and Debug modes; the error appears in both. Note I am only using the simulator and do not have a developer's license to try it on an actual device.
I have turned off (set to "None") compiler optimization (it was already off for the Debug build anyway).
I have tried building to iOS 8.0 with the same error results.
Any way to get around this issue is appreciated. A let hack = solution is acceptable, but preferably a solution will at least guarantee to not produce the error in the future under different build conditions.
Thanks!

Resources