Value of type "localSearch" has no member "start"? - swift

I'm having this issue in Xcode 7.3.1. I'm essentially adding a search bar to a map. Here's the first part of the code for context:
import UIKit
import MapKit
class FirstViewController: UIViewController, UISearchBarDelegate {
var searchController:UISearchController!
var annotation:MKAnnotation!
var localSearchRequest:MKLocalSearchRequest!
var localSearch:MKLocalSearch!
var localSearchResponse:MKLocalSearchResponse!
var error:NSError!
var pointAnnotation:MKPointAnnotation!
var pinAnnotationView:MKPinAnnotationView!
#IBAction func showSearchBar(sender: AnyObject) {
searchController = UISearchController(searchResultsController: nil)
searchController.hidesNavigationBarDuringPresentation = false
self.searchController.searchBar.delegate = self
presentViewController(searchController, animated: true, completion: nil)
}
#IBOutlet var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
let initialLocation = CLLocation(latitude: 47.6062, longitude: -122.3321)
let regionRadius: CLLocationDistance = 1000
func centerMapOnLocation(location: CLLocation) {
let coordinateRegion = MKCoordinateRegionMakeWithDistance(location.coordinate,
regionRadius * 2.0, regionRadius * 2.0)
mapView.setRegion(coordinateRegion, animated: true)
}
centerMapOnLocation(initialLocation)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
Here's the snippet where the error occurs:
func searchBarSearchButtonClicked(searchBar: UISearchBar){
searchBar.resignFirstResponder()
dismissViewControllerAnimated(true, completion: nil)
if self.mapView.annotations.count != 0{
annotation = self.mapView.annotations[0]
self.mapView.removeAnnotation(annotation)
}
localSearchRequest = MKLocalSearchRequest()
localSearchRequest.naturalLanguageQuery = searchBar.text
localSearch = MKLocalSearch(request: localSearchRequest)
localSearch.start{ (localSearchResponse, error) -> Void in
if localSearchResponse == nil{
let alertController = UIAlertController(title: nil, message: "Place Not Found", preferredStyle: UIAlertControllerStyle.alert)
alertController.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.default, handler: nil))
self.present(alertController, animated: true, completion: nil)
return
At localSearch.start, I am getting:
"Value of type MKLocalSearch has no member start"
I'm fairly new to Swift and pretty confused as to what the error is.
I also get:
"expected separator ",""
in the 3rd to last line. But even when changing it, error continues to occur. Could the two be related?

Since you are using older version of swift (swift 2.2) so I think you need to
replace
localSearch.start { ... //this is for swift 3
with
localSearch.startWithCompletionHandler { ... //this is for swift 2.2
For the error , of separator try this:
UIAlertActionStyle.Default
instead of
UIAlertActionStyle.default
Note : I think you also need to change UIAlertControllerStyle.alert to UIAlertControllerStyle.Alert

Related

GMSPlace returns invalid coordinate (-180, -180), but name and place ID are correct

I'm trying to implement an autocomplete search on Google Maps that will show the location that the user selects on the map with a marker.
Search works fine. The problem is as follows. When I select a location from the search results, I get a GMSPlace object that has the correct name as the selected value, correct place ID (confirmed using this link), but incorrect coordinates (-180.0,-180.0, or the kCLLocationCoordinate2DInvalid constant). I tested this on multiple locations.
Most of this code was borrowed from documentation for the Places API.
import UIKit
import GoogleMaps
import GooglePlaces
class ViewController: UIViewController, UISearchBarDelegate {
#IBOutlet weak var mapContainer: UIView!
var mapView: GMSMapView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.mapView = GMSMapView(frame: self.mapContainer.frame)
self.view.addSubview(self.mapView)
}
// Code from https://developers.google.com/places/ios-sdk/autocomplete#add_an_autocomplete_ui_control
#IBAction func searchByAddress(_ sender: Any) {
// Present the Autocomplete view controller when the button is pressed.
let autocompleteController = GMSAutocompleteViewController()
autocompleteController.delegate = self
// Specify the place data types to return.
let fields: GMSPlaceField = GMSPlaceField(rawValue: UInt(GMSPlaceField.name.rawValue) |
UInt(GMSPlaceField.placeID.rawValue))!
autocompleteController.placeFields = fields
// Display the autocomplete view controller.
present(autocompleteController, animated: true, completion: nil)
}
}
extension ViewController: GMSAutocompleteViewControllerDelegate {
// Handle the user's selection.
func viewController(_ viewController: GMSAutocompleteViewController, didAutocompleteWith place: GMSPlace) {
let position: CLLocationCoordinate2D = place.coordinate
let camera = GMSCameraPosition.camera(withLatitude: position.latitude, longitude: position.longitude, zoom: 10)
let newMapView = GMSMapView.map(withFrame: self.mapContainer.frame, camera: camera)
self.mapView = newMapView
self.view.addSubview(newMapView)
let marker = GMSMarker()
marker.position = position
marker.title = place.name
marker.map = self.mapView
viewController.dismiss(animated: true, completion: nil)
}
func viewController(_ viewController: GMSAutocompleteViewController, didFailAutocompleteWithError error: Error) {
// TODO: handle the error.
print("Error: ", error.localizedDescription)
}
// User canceled the operation.
func wasCancelled(_ viewController: GMSAutocompleteViewController) {
viewController.dismiss(animated: true, completion: nil)
}
// Turn the network activity indicator on and off again.
func didRequestAutocompletePredictions(_ viewController: GMSAutocompleteViewController) {
UIApplication.shared.isNetworkActivityIndicatorVisible = true
}
func didUpdateAutocompletePredictions(_ viewController: GMSAutocompleteViewController) {
UIApplication.shared.isNetworkActivityIndicatorVisible = false
}
}
Any help would be appreciated!
I was facing the same issue and went through the Place SDK documentation which says clearly that we should define before hand to the GMSPlaceField of what details do we need exactly and if you had followed the doc completely, it would be resulting only in the name and placeId being populated.
So while instantiating your GMSAutoCompleteViewController define in the following way.
**let fields: GMSPlaceField = GMSPlaceField(rawValue:UInt(GMSPlaceField.name.rawValue) |
UInt(GMSPlaceField.placeID.rawValue) |
UInt(GMSPlaceField.coordinate.rawValue) |
GMSPlaceField.addressComponents.rawValue |
GMSPlaceField.formattedAddress.rawValue)!
autocompleteController.placeFields = fields**

Getting an error when attempting to run the app in swift - Thread 1: EXC_BAD_ACCESS (code=2, address=0x7ffee4ffdee8)

I am new to Swift development, and trying to create a basic app where data is entered in one view controller, and is displayed in a table view in another. This is my code for the adding data view controller:
I am getting error:
Thread 1: EXC_BAD_ACCESS (code=2, address=0x7ffee4ffdee8) error on my line for line in which I define the "context" constant, i.e. let constant = (UIAppli...
Can anyone help me figure out why this is the case? Thank you so much!
import UIKit
import CoreData
class AddCardViewController: UIViewController {
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
#IBOutlet weak var nameTF: UITextField!
#IBOutlet weak var cardNumberTF: UITextField!
#IBOutlet weak var securityCodeTF: UITextField!
#IBOutlet weak var expiryDateTF: UITextField!
let prevVC = CardListViewController()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func addButtonPressed(_ sender: Any) {
let name = nameTF.text
let number = cardNumberTF.text
let key = securityCodeTF.text
let date = expiryDateTF.text
if (name?.isEmpty)! {
let alert = UIAlertController(
title: "Error",
message: "Please enter your full name",
preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(
title: "OK",
style: UIAlertActionStyle.default,
handler: nil))
present(alert, animated: true, completion: nil)
} else if (number?.isEmpty)! {
let alert = UIAlertController(
title: "Error",
message: "Please correctly enter your credit card number",
preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(
title: "OK",
style: UIAlertActionStyle.default,
handler: nil))
present(alert, animated: true, completion: nil)
} else if (key?.isEmpty)! {
let alert = UIAlertController(
title: "Error",
message: "Please correctly enter your security key",
preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(
title: "OK",
style: UIAlertActionStyle.default,
handler: nil))
present(alert, animated: true, completion: nil)
} else if (date?.isEmpty)! {
let alert = UIAlertController(
title: "Error",
message: "Please enter your card's expiry date",
preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(
title: "OK",
style: UIAlertActionStyle.default,
handler: nil))
present(alert, animated: true, completion: nil)
} else {
let newCard = CardsData(context: self.context)
newCard.id = Int32(prevVC.itemArray.count)
newCard.fullname = nameTF.text
newCard.cardnumber = cardNumberTF.text
prevVC.itemArray.append(newCard)
self.saveCard()
}
}
func saveCard() {
do {
try context.save()
} catch {
print("Error saving context \(error)")
}
performSegue(withIdentifier: "backToDBView", sender: self)
}
func loadCards() {
let request : NSFetchRequest<CardsData> = CardsData.fetchRequest()
do {
prevVC.itemArray = try context.fetch(request)
} catch {
print("Error fetching data from \(error)")
}
}
}
EDIT: Here's my code in the AppDelegate class as well:
import UIKit
import CoreData
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
return true
}
func applicationWillTerminate(_ application: UIApplication) {
self.saveContext()
}
// MARK: - Core Data stack
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "DataModel")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
// MARK: - Core Data Saving support
func saveContext () {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
}

MapKit annotations inconsistently deleting, don't know why

I am making a basic dummy app for myself because I am just starting with xcode and swift 3.
clearLastPin function should remove the most recent pin placed. Sometimes it does but more often than not it does not.
I'm not sure where there is such inconsistency, and the same goes for clearPins, which should remove ever pin from map.
#IBAction func clearLastPin(_ sender: UIBarButtonItem) {
newAnnotationCount = self.mapView.annotations.count - 1
//prints for testing
print("pressed")
print("new",newAnnotationCount)
print("mapView",self.mapView.annotations.count)
if newAnnotationCount != 0{
print("if statement entered")
annotation = self.mapView.annotations[self.mapView.annotations.count - 1]
self.mapView.removeAnnotation(annotation)
}
}
#IBAction func clearPins(_ sender: UIBarButtonItem) {
newAnnotationCount = self.mapView.annotations.count - 1
print("pressed")
while newAnnotationCount != 0{
print("while statement entered")
annotation = self.mapView.annotations[self.mapView.annotations.count - 1]
self.mapView.removeAnnotation(annotation)
newAnnotationCount = newAnnotationCount - 1
}
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar){
//1
searchBar.resignFirstResponder()
dismiss(animated: true, completion: nil) //the app will dismiss the presented search controller
//map view will look for any previously drawn annotation on the map and remove it
//2
//the ‘naturalLanguageQuery’ is very important in order to look up for -even an incomplete- addresses and POI (point of interests) like
//restaurants, Coffeehouse, etc.
localSearchRequest = MKLocalSearchRequest()
localSearchRequest.naturalLanguageQuery = searchBar.text //transforming the search bar text into a natural language query
localSearch = MKLocalSearch(request: localSearchRequest)
localSearch.start { (localSearchResponse, error) -> Void in
if localSearchResponse == nil{
let alertController = UIAlertController(title: nil, message: "Place Not Found", preferredStyle: UIAlertControllerStyle.alert)
alertController.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.default, handler: nil))
self.present(alertController, animated: true, completion: nil)
return
}
//3
//If the search API returns a valid coordinates for the place, then the app will instantiate a 2D point and draw it on the map within
//a pin annotation view
self.pointAnnotation = MKPointAnnotation()
self.pointAnnotation.title = searchBar.text
self.pointAnnotation.coordinate = CLLocationCoordinate2D(latitude: localSearchResponse!.boundingRegion.center.latitude, longitude: localSearchResponse!.boundingRegion.center.longitude)
self.pinAnnotationView = MKPinAnnotationView(annotation: self.pointAnnotation, reuseIdentifier: nil)
self.mapView.centerCoordinate = self.pointAnnotation.coordinate
self.mapView.addAnnotation(self.pinAnnotationView.annotation!)
//: zooms in on coordinate of found location
/*
let span:MKCoordinateSpan = MKCoordinateSpanMake(0.01, 0.01) // how far zoomed in
let myLocation:CLLocationCoordinate2D = CLLocationCoordinate2DMake(localSearchResponse!.boundingRegion.center.latitude, localSearchResponse!.boundingRegion.center.longitude) // users location
let region:MKCoordinateRegion = MKCoordinateRegionMake(myLocation, span) // links the two, I guess
self.mapView.setRegion(region, animated: true) // maps region is equal to region we just made
*/
}
}

NSUserDefaults errors on Swift 2.0

For some reasons, I can't seem to get my NSUserDefaults to work correctly under Swift 2.0. It work fine under the older version, but under Swift 2.0, it doesn't work. I know the coding has been changed for Swift 2.0, but for some reason, all the information filled in the tableview goes away once I leave that page. Any suggestions?
import UIKit
class MainTableViewController: UITableViewController, UITextFieldDelegate {
#IBOutlet weak var name: UITextField!
#IBAction func btnSave() {
let userDefaults = NSUserDefaults.standardUserDefaults()
if name.text == "" {
let alert = UIAlertController(title: "Data", message: "Missing Name.", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
let userDefaults = NSUserDefaults.standardUserDefaults()
NSUserDefaults.standardUserDefaults().setObject(name, forKey:"name")
userDefaults.synchronize()
}
override func viewDidLoad() {
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject(" ", forKey: "name")
super.viewDidLoad()
}
}
Perhaps try this
import UIKit
class MainTableViewController: UITableViewController, UITextFieldDelegate {
let userDefaults = NSUserDefaults.standardUserDefaults()
#IBOutlet weak var name: UITextField!
#IBAction func btnSave() {
if name.text == "" {
let alert = UIAlertController(title: "Data", message: "Missing Name.", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
} else {
userDefaults.setObject(name.text, forKey:"name")
userDefaults.synchronize()
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(animated: Bool) {
name.text = userDefaults.stringForKey("name")
}
}
In your viewDidLoad you're erasing the info in name, so everytime you enter your view, it will get deleted.
override func viewDidLoad() {
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject(" ", forKey: "name")
super.viewDidLoad()
}
Also, since name is a UITextField and not a String, when you're saving your name, it should be done like this:
NSUserDefaults.standardUserDefaults().setObject(name.text, forKey:"name")
instead of this:
NSUserDefaults.standardUserDefaults().setObject(name, forKey:"name")
Also, inside your btnSave() method, you're only saving the name.text if it's equal to "". So if you enter any other information, it won't be saved. I'd recommend modifying it so it looks something like this (you could use guard instead of if name.text also):
#IBAction func btnSave() {
if name.text == "" {
let alert = UIAlertController(title: "Data", message: "Missing Name.", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
return
}
let userDefaults = NSUserDefaults.standardUserDefaults()
userDefaults.setObject(name.text, forKey:"name")
userDefaults.synchronize()
}
P.S: Remember that NSUserDefaults is intended for user preferences. I'd recommend against saving large amounts of data into NSUserDefaults.

Black background image using camera with Swift2

I'm trying to display the user camera (back camera) using the AVFoundation, but I must be doing something wrong because It's only showing a black background image.
I have checked my Privacy > Camera and there isn't any option regarding the camera with my App, and I am not able to display the .Alert action to ask the user the permission to access the camera.
Here is my code, I hope you could help me because this is very weird:
import UIKit
import AVFoundation
class CodigoBarrasViewController: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
#IBOutlet weak var messageLabel:UILabel!
#IBOutlet weak var imagenFondo:UIImageView!
#IBOutlet weak var BackgroundView:UIView!
var string:String!
var captureSession:AVCaptureSession?
var videoPreviewLayer:AVCaptureVideoPreviewLayer?
var qrCodeFrameView:UIView?
// Added to support different barcodes
let supportedBarCodes = [AVMetadataObjectTypeQRCode, AVMetadataObjectTypeCode128Code, AVMetadataObjectTypeCode39Code, AVMetadataObjectTypeCode93Code, AVMetadataObjectTypeUPCECode, AVMetadataObjectTypePDF417Code, AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeAztecCode]
override func viewDidAppear(animated: Bool) {
captureSession?.startRunning()
self.qrCodeFrameView?.hidden = true
}
override func viewDidLoad() {
//captureSession?.startRunning()
super.viewDidLoad()
// Get an instance of the AVCaptureDevice class to initialize a device object and provide the video
// as the media type parameter.
let captureDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
do {
input = try AVCaptureDeviceInput(device: captureDevice) as AVCaptureDeviceInput
}
catch let error as NSError {
print(error)
}
// Initialize the captureSession object.
captureSession = AVCaptureSession()
// Set the input device on the capture session.
captureSession?.addInput(input)
//captureSession?.addInput(input as AVCaptureInput)
// Initialize a AVCaptureMetadataOutput object and set it as the output device to the capture session.
let captureMetadataOutput = AVCaptureMetadataOutput()
captureSession?.addOutput(captureMetadataOutput)
// Set delegate and use the default dispatch queue to execute the call back
captureMetadataOutput.setMetadataObjectsDelegate(self, queue: dispatch_get_main_queue())
captureMetadataOutput.metadataObjectTypes = supportedBarCodes
// Initialize the video preview layer and add it as a sublayer to the viewPreview view's layer.
videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
videoPreviewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill
videoPreviewLayer?.frame = view.layer.bounds
view.layer.addSublayer(videoPreviewLayer!)
// Start video capture.
captureSession?.startRunning()
// Move the message label to the top view
view.bringSubviewToFront(imagenFondo)
view.bringSubviewToFront(messageLabel)
view.bringSubviewToFront(BackgroundView)
// Initialize QR Code Frame to highlight the QR code
qrCodeFrameView = UIView()
qrCodeFrameView?.layer.borderColor = UIColor(hex: 0x00B7BB).CGColor
qrCodeFrameView?.layer.borderWidth = 2
view.addSubview(qrCodeFrameView!)
view.bringSubviewToFront(qrCodeFrameView!)
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
//self.navigationController?.hidesBarsOnSwipe = true
self.navigationController?.setNavigationBarHidden(true, animated: false)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func captureOutput(captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [AnyObject]!, fromConnection connection: AVCaptureConnection!) {
// Check if the metadataObjects array is not nil and it contains at least one object.
if metadataObjects == nil || metadataObjects.count == 0 {
qrCodeFrameView?.frame = CGRectZero
//messageLabel.text = "No QR code is detected"
return
}
else
{
// Get the metadata object.
let metadataObj = metadataObjects[0] as! AVMetadataMachineReadableCodeObject
// Here we use filter method to check if the type of metadataObj is supported
// Instead of hardcoding the AVMetadataObjectTypeQRCode, we check if the type
// can be found in the array of supported bar codes.
if supportedBarCodes.filter({ $0 == metadataObj.type }).count > 0 {
// If the found metadata is equal to the QR code metadata then update the status label's text and set the bounds
let barCodeObject = videoPreviewLayer?.transformedMetadataObjectForMetadataObject(metadataObj as AVMetadataMachineReadableCodeObject) as! AVMetadataMachineReadableCodeObject
qrCodeFrameView?.frame = barCodeObject.bounds
if metadataObj.stringValue != nil {
captureSession?.stopRunning()
self.qrCodeFrameView?.hidden = false
launchApp(metadataObj.stringValue)
}
}
}
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if segue.identifier == "seeProduct" {
let destinationController = segue.destinationViewController as! ProductoCamViewController
let string = (sender as! String!)
let backItem = UIBarButtonItem()
backItem.title = " "
navigationItem.backBarButtonItem = backItem
destinationController.ean = string
}
}
func launchApp(decodedURL: String) {
let alertPrompt = UIAlertController(title: nil, message: nil, preferredStyle: .ActionSheet)
//let alertPrompt = UIAlertController(title: "", message: decodedURL, preferredStyle: .ActionSheet)
let confirmAction = UIAlertAction(title: "See product", style: UIAlertActionStyle.Default, handler: { (action) -> Void in
self.performSegueWithIdentifier("seeProduct", sender: decodedURL)
})
let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Default, handler: { (action) -> Void in
self.captureSession?.startRunning()
self.qrCodeFrameView?.hidden = true
})
//let cancelAction = UIAlertAction(title: "Cancelar", style: UIAlertActionStyle.Cancel, handler: nil)
alertPrompt.addAction(confirmAction)
alertPrompt.addAction(cancelAction)
self.presentViewController(alertPrompt, animated: true, completion: nil)
}
}
Thanks in advance,
Regards.
I would suggest taking a look at UIImagePickerControllerDelegate if you're wanting to access the camera.
Implement this and all of the permission alerts are handled for you