Location Manager Crashing when No Internet - objective-c

The program runs fine when the user is connected to the internet. However, when the user is not connected, the app crashes with:
Error:The operation couldn’t be completed. (kCLErrorDomain error 8.)
fatal error: unexpectedly found nil while unwrapping an Optional value
I'd like the catch the error before it crashes and display an alertcontroller
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
CLGeocoder().reverseGeocodeLocation(manager.location, completionHandler: { (placemarks, error) -> Void in
if (error != nil) {
println("Error:" + error.localizedDescription)
}
if placemarks.count > 0 {
let pm = placemarks[0] as CLPlacemark
self.displayLocationInfo(pm)
currentLoc = manager.location
currentLocGeoPoint = PFGeoPoint(location:currentLoc)
var query = PFQuery(className:"Restaurant") //default: Restaurant
query.whereKey("RestaurantLoc", nearGeoPoint:currentLocGeoPoint, withinMiles:setDistanceMiles) //filter by miles
query.limit = 1000 //limit number of results
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]!, error: NSError!) -> Void in
if objects.isEmpty {
//NO RESULTS
let alertController = UIAlertController(title: "Unavailable Area", message:
"This app is not available in your area yet. We'll try our best to have it up and running as soon as possible.", preferredStyle: UIAlertControllerStyle.Alert)
alertController.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default,handler: nil))
self.presentViewController(alertController, animated: true, completion: nil)
self.navigationController!.popViewControllerAnimated(true)
} else {
newArray = objects
}
self.hideActivityIndicator(self.view)
}
} else {
println("error: \(error)")
}
})
}

The problem is your if statement. You are saying this:
if (error != nil) {
println("Error:" + error.localizedDescription)
}
if placemarks.count > 0 { // ...
Well, there was an error. And you were told that there was an error. But you didn't listen! You didn't stop! You just go right on as if nothing had happened, and try to access the placemarks variable - which wasn't returned. You need to use logic here so that you exit if there's an error:
if (error != nil) {
println("Error:" + error.localizedDescription)
return // go no further!
}
if placemarks.count > 0 { // ...
Note, however, that it would be better to check placemarks for nil first:
if placemarks == nil {
// check error
// return
}
if placemarks.count > 0 { // ...

I believe you need to implement locationManager:didFailWithError which tells the delegate when it is unable to retrieve the location. Then, you just need to handle the error with appropriate response.
For references: CLLocationManagerDelegate apple docs

Related

What happens in Dropbox API for iOS (Swift) if I create a folder that already exists?

The first time I do it, I want the folder created.
The next time, should I check if folder already exists, or just do the create folder and assume nothing happens?
You can do this either way:
Check if the folder exists before attempting to create it:
client.files.getMetadata(path: folderPath).response { response, error in
if let _ = response {
print("Something already exists at path.")
} else if let error = error {
switch error {
case .routeError(let boxed, _, _, _):
switch boxed.unboxed as Files.GetMetadataError {
case .path(let lookupError):
switch lookupError {
case .notFound:
print("Nothing was found at this path. Attempting to create folder...")
client.files.createFolderV2(path: folderPath).response { response, error in
if let response = response {
print("Created folder:")
print(response)
} else if let error = error {
print("Error creating folder: ")
print(error)
}
}
default:
print("Some other error occurred.")
// handle accordingly...
}
}
default:
print("Some other error occurred.")
// handle accordingly...
}
}
}
Always attempt to create the folder, and handle the error that occurs if it already exists:
client.files.createFolderV2(path: folderPath).response { response, error in
if let response = response {
print("Created folder:")
print(response)
} else if let error = error {
print("Error creating folder: ")
print(error)
// handle accordingly...
}
}

FBSDKLoginManagerLoginResult return nil iOS 11

With iOS 11 FBSDKLoginManager (v.4.25.0) use SFAuthenticationSession instead of SFSafariViewController, if user cancel Sign In, FBSDKLoginManagerLoginResult always return nil and result.isCancelled code not work:
[[FBSDKLoginManager new] logInWithReadPermissions:#[PERMISSIONS]
fromViewController:self
handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) {
if (error) {
//Error happen
}
else if (result.isCancelled) {
//User cancel
}
else {
//Login success
}
}];
In this case always happen error with description 'com.apple.SafariServices.Authentication Code=1 "(null)"'. So, in this case, we can't discern really error from SFAuthenticationSession error. Any ideas how to handle different errors? Or just need to wait FBSDK update?
I'm using FBSDK version [ 4.17.0 ] with ios 11 and it works fine
let facebookLogin = FBSDKLoginManager()
facebookLogin.logOut()
facebookLogin.logIn(withReadPermissions: ["public_profile","email", "user_friends"], from:self, handler:
{
(facebookResult, facebookError) -> Void in
if facebookError != nil
{
print("Facebook login failed. Error \(String(describing: facebookError))")
}
else if (facebookResult?.isCancelled)!
{
print("Facebook login was cancelled.")
}
else
{
}
})

session.dataTask synchronization issue

I have some trouble with the code below.
Though it works, there is some timing problem.
First let me say what I expect, I suppose the completion handler should be run when the data download is complete and my image ready to use. But reality seems to be quite different. When I try it the completion handler is called right away (I can see 'All OK' in the console) as if everything was instantaneous. But the image gets actually displayed much later. What am I missing?
let imageURL = URL(string: myURLString)
session = URLSession.shared,
_ = session.dataTask(with: imageURL) {[weak self]
(data: Data?, response: URLResponse?, error: Error?) in
if error == nil {
print("All OK")
self?.theImage = UIImage(data: data!)
self?.theView.image = self?.theImage
} else {print(error!)}
DispatchQueue.main.async {
self?.activityIndicator.stopAnimating()
self?.theView.setNeedsDisplay()
}
}.resume()
Can you try this code?
The control should not be actually going inside the handler at first call. And I think there are a few mistakes in your code as well which I pointed out earlier, especially the main thread is required for updating UI.
let session : URLSession
let config = URLSessionConfiguration.default
var resultFromServer: Any?
let responseResultData = [String:Any]()
session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
session.dataTask(with: request) { (data, response, error ) in
if error != nil {
DispatchQueue.main.async(execute: {
session.invalidateAndCancel()
})
}else{
let httpResponse: HTTPURLResponse = response as! HTTPURLResponse
do{
resultFromServer = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers)
if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 || httpResponse.statusCode == 202 || httpResponse.statusCode == 204 || httpResponse.statusCode == 203 {
if let respArr = resultFromServer as? [Any]{
//resp is array
}else if let respdict = resultFromServer as? [String : Any] {
//resp is dict
}else{
//resp is something else maybe string, etc
}
}
else {
//error status code something like 500, 404, etc
}
}
catch let error as NSError {
DispatchQueue.main.async(execute: {
session.invalidateAndCancel()
})
}
}
session.finishTasksAndInvalidate()
}.resume()

Alamofire Decodable serializer

I am trying to update some code for a Alamofire custom response serializer I found this bit of code on "bits of cocoa".
extension Alamofire.Request {
public func responseCollection<T: Decodable>(completionHandler: Response<[T], NSError> -> Void) -> Self {
let responseSerializer = ResponseSerializer<[T], NSError> { request, response, data, error in
guard error == nil else { return .Failure(error!) }
let result = Alamofire
.Request
.JSONResponseSerializer(options: .AllowFragments)
.serializeResponse(request, response, data, error)
switch result {
case .Success(let value):
do {
return .Success(try [T].decode(value))
} catch {
return .Failure(Error.errorWithCode(.JSONSerializationFailed,
failureReason: "JSON parsing error, JSON: \(value)"))
}
case .Failure(let error): return.Failure(error)
}
}
return response(responseSerializer: responseSerializer, completionHandler: completionHandler)
}
}
This is pre swift 3, and Response<[T], NSError> is now a single value specialization Response<[T]> because of this I am not sure how this extension would translate for the changes to Alamofire on the swift 3
I started to update this code this is as far as I got
extension Alamofire.Request {
public func responseCollection<T: Decodable>(completionHandler: (Response<[T]>) -> Void) -> Self {
let responseSerializer = ResponseSerializer<[T]> { request, response, data, error in
guard error == nil else { return .failure(error!) }
let result = Alamofire
.Request
.JSONResponseSerializer(options: .allowFragments)
.serializeResponse(request, response, data, error)
switch result {
case .success(let value):
do {
return .success(try [T].decode(value))
} catch {
return .failure(Error(.errorWithCode(.JSONSerializationFailed, failureReason: "JSON parsing error, JSON: \(value)")))
}
case .failure(let error): return.failure(error)
}
}
return response(responseSerializer: responseSerializer, com
pletionHandler: completionHandler)
}
}
this get me 2 errors that at the moment I have not found any way to fix them:
1) for "return .failure(Error(.errorWithCode(.JSONSerializationFailed, failureReason: "JSON parsing error, JSON: \(value)")))", I am getting this error ('Error' cannot be constructed because it as no accessible initializers)
2) for "return response(responseSerializer: responseSerializer, completionHandler: completionHandler)", I am getting this error (Cannot call value of non-function type 'HTTPURLResponse')
Hopefully if any one can point me to a better solution then this bit of code or the correct fix for this. Thanks I will be working on this still, if I do fix it I will update this ticket.
Edit - update
So This is the code as of now
extension Alamofire.Request {
public func responseDecodable<T: Decodable>(completionHandler: #escaping (Response<T>) -> Void) -> Self {
let responseSerializer = ResponseSerializer<T> { request, response, data, error in
guard error == nil else {
print("error Network request: \(error)")
return .failure(error!)
}
let result = Alamofire
.Request
.JSONResponseSerializer(options: .allowFragments)
.serializeResponse(request, response, data, error)
switch result {
case .success(let value):
do {
let decodableObject = try T.decode(value)
return .success(decodableObject)
} catch let decodeErr {
print(decodeErr)
let failureReason = "JSON parsing error, JSON: \(value)"
let userInfo = [NSLocalizedFailureReasonErrorKey: failureReason]
let error = NSError(domain: "com.prospects.error", code: BackendError.JSONSerializationFailed.rawValue, userInfo: userInfo)
return .failure(error)
}
case .failure(let error): return.failure(error)
}
}
return response(responseSerializer: responseSerializer, completionHandler: completionHandler)
}
}
public protocol ResponseObjectSerializable {
init?(response: HTTPURLResponse, representation: AnyObject)
}
as per last comment : Response and ResponseSerializer are now unresolved, this used to work with no error yesterday. but updating xcode and alamofire this morning as made this to get errors now.

Objective-C to Swift: Facebook iOS API requestUserInfo method

Hi I am trying to figure out how to rewrite this in swift:
- (IBAction)requestUserInfo:(id)sender
{
// We will request the user's public picture and the user's birthday
// These are the permissions we need:
NSArray *permissionsNeeded = #[#"public_profile", #"user_birthday", #"email"];
// Request the permissions the user currently has
[FBRequestConnection startWithGraphPath:#"/me/permissions" completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if (!error){
// Parse the list of existing permissions and extract them for easier use
NSMutableArray *currentPermissions = [[NSMutableArray alloc] init];
NSLog(#"The fucking class is: %#", [result class]);
NSArray *returnedPermissions = (NSArray *)[result data];
for (NSDictionary *perm in returnedPermissions) {
if ([[perm objectForKey:#"status"] isEqualToString:#"granted"]) {
[currentPermissions addObject:[perm objectForKey:#"permission"]];
}
} // cut cut here
}
EDIT:
I was having trouble trying to get the required data out of the FBGraphObject but figured it out after some further inspection. I have posted the swift version below so that people can just cut and paste it and get on with using swift. Hope it saves someone some time.
Here:
#IBAction func requestUserInfo(sender: AnyObject){
// These are the permissions we need:
var permissionsNeeded = ["public_profile", "user_birthday", "email"]
// Request the permissions the user currently has
FBRequestConnection.startWithGraphPath("/me/permissions", completionHandler: {(connection, result, error) -> Void in
if error == nil{
// Parse the list of existing permissions and extract them for easier use
var theResult = result as? [String:[AnyObject]]
var currentPermissions = [String]()
let returnedPermissions = theResult?["data"] as [[String:String]]
for perm in returnedPermissions {
if perm["status"] == "granted" {
currentPermissions.append(perm["permission"]!)
}
}
// Build the list of requested permissions by starting with the permissions
// needed and then removing any current permissions
println("Needed: \(permissionsNeeded)")
println("Current: \(currentPermissions)")
var requestPermissions = NSMutableArray(array: permissionsNeeded, copyItems: true)
requestPermissions.removeObjectsInArray(currentPermissions)
println("Asking: \(requestPermissions)")
// TODO PUT A POPUP HERE TO TELL WHAT PERMISSIONS WE NEED!
// If we have permissions to request
if requestPermissions.count > 0 {
// Ask for the missing permissions
FBSession.activeSession().requestNewReadPermissions(requestPermissions, completionHandler: {(session, error) -> Void in
if (error == nil) {
// Permission granted, we can request the user information
self.makeRequestForUserData()
} else {
// An error occurred, we need to handle the error
// Check out our error handling guide: https://developers.facebook.com/docs/ios/errors/
println("error: \(error?.description)")
}
})
} else {
// Permissions are present
// We can request the user information
self.makeRequestForUserData()
}
} else {
// An error occurred, we need to handle the error
// Check out our error handling guide: https://developers.facebook.com/docs/ios/errors/
println("error: \(error?.description)")
}
})
}
private func makeRequestForUserData() {
FBRequestConnection.startForMeWithCompletionHandler({(connection, result, error) -> Void in
if (error == nil) {
// Success! Include your code to handle the results here
println("user info: \(result)")
} else {
// An error occurred, we need to handle the error
// Check out our error handling guide: https://developers.facebook.com/docs/ios/errors/
println("error: \(error?.description)")
}
})
}
// Show an alert message
func showMessage(text : NSString, title : NSString){
var alert = UIAlertController(title: title, message: text, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
UIApplication.sharedApplication().delegate?.window!?.rootViewController?.presentViewController(alert, animated: true, completion: nil)
}