Swift - Closure in Dictionary Crash - ios

So I had objc code which uses Singleton instance to make pings(using SimplePing) and holds ping request completion blocks. In ObjC it looks like this:
#interface SimplePingClient()
{
NSMutableArray* _currentPings;
NSMutableDictionary* _currentCallbacks;
}
#end
#implementation SimplePingClient
-(id)init
{
if( self = [super init] )
{
_currentPings = [NSMutableArray new];
_currentCallbacks = [NSMutableDictionary new];
}
return self;
}
method to start ping:
-(void)pingHostname:(NSString*)hostName andResultCallBlock:(void(^)(NSString* latency))result
{
TSimplePing* pingClient = [TSimplePing simplePingWithHostName:hostName];
[_currentPings addObject:pingClient];
[_currentCallbacks setObject:result forKey:[NSValue valueWithNonretainedObject:pingClient]];
pingClient.delegate = self;
//some other irrelevant code
...
}
And when SimplePing delegate method is called:
- (void)simplePing:(TSimplePing *)pinger didReceivePingResponsePacket:(NSData *)packet
{
void(^callback)(NSString* latency) = [_currentCallbacks objectForKey:[NSValue valueWithNonretainedObject:pinger]];
if( callback )
{
//some irrelevant code
...
callback(#"123");//hard coded for test, irrelevant code get this value correctly ;)
[_currentCallbacks removeObjectForKey:[NSValue valueWithNonretainedObject:pinger]];
}
[_currentPings removeObject:pinger];
}
So this code work flawlessly on objc. However when I try to port this in Swift I keep get error EXC_BAD_ACCESS. To simplify example I just erased everything and it turns out that storing Closure in Dictionary and immediately call it is not working for me:
typealias callbackClosure = (String) -> ()
class SimplePingClient: NSObject
{
// MARK: variables
var _currentPings = [TSimplePing]()
var _currentCallbacks: Dictionary<String, callbackClosure> = [String: callbackClosure]()
private func ping(hostname: String, resultCallback:callbackClosure)
{
var pingClient = TSimplePing(hostName: hostname)
pingClient.delegate = self
_currentPings.append(pingClient)
_currentCallbacks[pingClient.hostName] = resultCallback
if let callback = _currentCallbacks[pingClient.hostName]
{
callback("123213")//here program CRASHES
}
}
}
As you can see in code I even try using hostname(which is String) instead of NSValue assuming some memory problems, but it's not the case - still crashing. This error however is surely something with memory management, however I cannot understand what I'm doing wrong. If anyone could point me what I'm missing I'll really apreciate it.

As #Owen Hartnett suggested I'm moving solution from answer itself here:
Okay, after another hour of debugging it turns out reason is pretty silly. As I said I'm porting my code, so previously I was using this class as bridged objective-c code, therefore this function:
-(void)pingHostname:(NSString*)hostName andResultCallBlock:(void(^)(NSString* latency))result
was bridged following way:
SimplePingClient.pingHostname(latencyUrl, refreshTime: refreshRate) { (latency: String!) -> () in
if nil != latency
{
//dummy code
}
}
therefore after SimplePingClient became swift class, my new closure signature is(note that now latency is not force unwrapped and is not optional, and also no need to check against nil)
SimplePingClient.pingHostname(latencyUrl, refreshTime: refreshRate) { (latency: String) -> () in
//dummy code
}
So by simply changing method call everything started working. Strange thing that compiler doesn't notice this problem though.

Related

Swift: how to detect the error type using the debugger?

I'm new in iOS development, so maybe I'm thinking in the wrong way. I coded a view model with a function that calls an API, and everything works fine.
class SearchCityViewModel : ViewModelProtocol {
//OBSERVABLES
var cities = PublishSubject<[City]>()
var networkError = PublishSubject<Void>()
var generalError = PublishSubject<Void>()
init(){
print("Init SearchCityViewModel")
reinit()
}
func reinit(){}
func searchCity(stringToSearch: String){
async {
do {
if stringToSearch.count>=2 {
let cities = try await(api.getCities(cityToSearch: stringToSearch)).payload!
self.cities.onNext(cities)
}
else {
self.cities.onNext([])
}
}
catch {
self.generalError.onNext(Void())
}
}
}
Now I want to handle errors. In the catch block I want to distinguish all the errors I want to handle gracefully, and for the other ones I just want to emit a general error. To do that, firstly I need to know which error is thrown when the situation I want to handle occurs. I usually do this with the debugger. For instance, I disable the internet connection, and i create a breakpoint inside the catch block. The idea is to check which error is thrown when the internet connection is disabled, in order to create a catch block for that kind of error.
Image of the debugger
I'm struggling because with the debugger I only see that is an AFError instance, but it's not telling me nothing more that can help me to catch it.
What is wrong with my workflow? Do I really need to read all the docs every time? For each library I use?
Thank you!
Perhaps you can read the articles and then you will know how to do it better, you can use the framework -oslog instead of using print function.
debugging your logging info
I found the way. What I was missing is casting the error as NSError. In this way, with the debugger is possible to see the domain and the code of the error. In the case of Alamofire, the real error is wrapped, and it's accessible through the underlyingError attribute. Once I had the domain and the code of the error, I wrote the following code:
class SearchCityViewModel : ViewModelProtocol {
//OBSERVABLES
var cities = PublishSubject<[City]>()
var networkError = PublishSubject<Void>()
var generalError = PublishSubject<Void>()
init(){
print("Init SearchCityViewModel")
reinit()
}
func reinit(){}
func searchCity(stringToSearch: String){
async {
do {
if stringToSearch.count>=2 {
let cities = try await(api.getCities(cityToSearch: stringToSearch)).payload!
self.cities.onNext(cities)
}
else {
self.cities.onNext([])
}
}
catch {
if let afError = asAFError, let underlyingError = afError.underlyingError as NSError?, underlyingError.domain == NSURLErrorDomain, underlyingError.code == NSURLErrorNotConnectedToInternet || underlyingError.code == NSURLErrorTimedOut {
self.networkError.onNext(Void())
}
else {
self.generalError.onNext(Void())
}
}
}
}

Calling Wrapper Function From Javascript is Crashing App - React-Native

Hello I am trying to call a function in javascript that I am exporting via Objective C. When I call my function in javascript my app is crashing.
RCT_EXPORT_METHOD(getModelAsync:()
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
NSError *error;
NSString *contents = [[UIDevice currentDevice] model];
if (contents) {
resolve(contents);
} else {
reject(#"Test", #"Something is broken",error);
}
}
Its failing on this on the if statement with this Error: Thread 1: EXC_BAD_ACCESS (code=1, address=0x0)
if (!RCTIsIdentifierHead(**input)) {
return NO;
All help is welcome, thanks!
I came across this issue today and managed to solved it. It looks like the function argument types aren't matching up. This error seems to be triggered, when the function types aren't compatible. Something like the following code snippet, would trigger this error, because a dictionary is not compatible with type string and thus the function argument cannot be correctly cast.
Module.m
RCT_EXTERN_METHOD(myFunction: (NSDictionary)options)
Module.swift
#objc
func myFunction(_ options: String) -> Void {
...
}
To fix it, make sure you are doing something like this:
FixedModule.m
RCT_EXTERN_METHOD(myFunction: (NSDictionary)options)
FixedModule.swift
#objc
func myFunction(_ options: NSDictionary) -> Void {
...
}
I hope this helps!

Testable Asynchronous Design Pattern in Swift

I'm learning Test Driven Development in Swift. I hit a wall when I realized the delegate pattern I regularly use for asynchronous requests is difficult to test. I've learned that if something's difficult to test, the design pattern behind the implementation could probably be better. This is confusing me because I think the delegate pattern I'm using is common and I'm wondering how others have dealt with this issue.
The pattern:
I wrote a service, which executes an asynchronous request in a static function which takes a delegate instance. The delegate instance conforms to a protocol which requires implementation of a success and failure method. I've contrived an example which hits Google.com. Please ignore the Type safety issues in this example. The actual code I'm running to hit an endpoint and parse JSON is safer. I just wanted to come up with a very small snippet of code to depict the issue that's causing difficulty while testing:
protocol GoogleServiceDelegate {
func gotGoogle(str: String);
func gotError(str: String);
}
struct GoogleService {
static func getGoogle(delegate: GoogleServiceDelegate) {
let url: NSURL! = NSURL(string: "http://google.com")
NSURLSession.sharedSession().dataTaskWithURL(url) { data, response, error in
if let data = data {
let str: NSString! = NSString(data: data, encoding: NSUTF8StringEncoding)
delegate.gotGoogle(str as String)
} else {
delegate.gotError("\(error)")
}
}
}
}
Here's the test which illustrates the problem:
class AsyncTestingTests: XCTestCase {
func testExample() {
let responseExpectation = expectationWithDescription("Got google response!")
struct GoogleDelegate: GoogleServiceDelegate {
func gotGoogle(str: String) {
// expectations about response
responseExpectation.fulfill()
}
func gotError(str: String) {
// expectations about error
responseExpectation.fulfill()
}
}
let myGoogleServiceDelegate = GoogleDelegate()
GoogleService.getGoogle(myGoogleServiceDelegate)
waitForExpectationsWithTimeout(5) { _ in
print("Never got a response from Google :(")
}
}
}
The problem arises at the two .fulfill() lines. I get the following error from Xcode:
Struct declaration cannot close over value 'responseExpectation' defined in outer scope
I understand the error, but am unsure what to adjust... Is there a workaround for this which I can use in the test, or is there a better (easily testable) pattern for asynchronous callbacks than what I am attempting? If you know of a better testable solution, would you mind taking the time to write down an example?
Yes, you can not close over variables defined outside of struct, to workaround, we need to use closures/functions and pass it to the struct. Methods in struct can invoke it when they receive the response.
func testExample() {
let responseExpectation = expectationWithDescription("Got google response!")
//Let a function capture the fulfilling of the expectation
func fullFillExpectation(){
responseExpectation.fullFill()
}
struct GoogleDelegate: GoogleServiceDelegate {
var fullFiller : (()->Void)!
func gotGoogle(str: String) {
// expectations about response via invoke the closure
fullFiller()
}
func gotError(str: String) {
// expectations about error - invoke the closure
fullFiller()
}
}
//Create the delegate with full filler function.
let myGoogleServiceDelegate = GoogleDelegate(fullFiller: fullFillExpectation)
GoogleService.getGoogle(myGoogleServiceDelegate)
waitForExpectationsWithTimeout(5) { _ in
print("Never got a response from Google :(")
}
}
}
PS: I could not test this, please test and let me know.

Parse iOS - How to capture the findObjects() error being thrown?

I am coding in Swift and attempting to use the findObjects() function for the Parse iOS SDK. However, I can't seem to figure out what type of error is being thrown by Parse if this function call fails. I'm a novice in Swift so that may be my issue. I attempt to call the function in a do->catch block and use the try keyword on the function call however I'm not sure what to catch. I can catch the error using the _ but I would like to grab the description from the error. Thanks!
P.S. I don't want to use the findObjectsInBackground() method.
do {
let object = try query.getFirstObject()
// do something with the object
} catch _ {
// this is where I would like to print out the error description
}
In Obj-C, which I assume will be similar, I print out the error.userInfo[#"error"] parameter of the NSError that is returned.
All you need is print(error). An example here:
func getReferenceNumberAsStringSync() -> String? {
let query = PFQuery(className: "PropertyCount")
do {
let object = try query.getFirstObject()
if let referenceNumber = object["count"] as? Int {
return String(referenceNumber)
}
} catch {
print(error)
}
return nil
}

Swift segmentation fault 11 due to variable scope

The following code caused the IDE in Xcode to fail, and swiftc throws segmentation fault (11):
func testDeviceWillNotify()
{
let expectation = expectationWithDescription("Will be ready.")
class FooMock: Foo
{
func accessoryDidConnect()
{
expectation.fulfill()
}
}
// ...
}
If I comment out the expectation.fulfill() everything is works correctly. I have also tried expectation! and expectation? but any reference to expectation caused Xcode to crash.
Using Xcode 6.1 (6A1052d) and its extremely frustrating. Does anyone know a way around this bug? Even writing the test another way will have to do.
I don't know how it your code suppose to work because the inner class have to automatically capture variables in the method scope.
This is a workaround
class FooMock: Foo
{
var accessoryDidConnectFunc : (Void -> Void)?
func accessoryDidConnect()
{
accessoryDidConnectFunc ?()
}
}
func testDeviceWillNotify()
{
let expectation = expectationWithDescription("Will be ready.")
let foo = FooMock()
foo.accessoryDidConnectFunc = { expectation.fulfill() }
// ...
}

Resources