got error on xcode7... I can't use 'new()' on xcode7? - swift

The error said that 'new()' is unavailable for Swift::use object initializers instead. Don't know how to fix...
func showImage(notif:NSNotification) {
if let userInfo = notif.userInfo as NSDictionary! {
//the code below is the one i got error for
let photos:NSMutableArray = NSMutableArray.new()
let photo:IDMPhoto = IDMPhoto(image: userInfo["img"] as! UIImage!)
photos.addObject(photo)
let browser:IDMPhotoBrowser = IDMPhotoBrowser(photos: photos as [AnyObject]!)
browser.delegate = self
browser.displayActionButton = false
self.presentViewController(browser, animated: true, completion: nil)
}
}

Simply change NSMutableArray.new() to NSMutableArray().

Related

Firestore pagination using MVVM architecture swift

I don't quite understand what I am doing wrong since I am very new to MVVM. It worked in MVC architecture. I've setup my VM and am able to get the first set of results and even then that's not working properly. I get 4 results instead of 10 which is what LOADLIMIT is set as. I was able to get it to work in an MVC architecture without any issues. The VM function which triggers the query is called multiple (3) times instead of just once i.e. even prior to scrolling.
Here is my VM:
enum FetchRestaurant {
case success
case error
case location
case end
}
class ListViewModel {
let restaurant: [Restaurant]?
let db = Firestore.firestore()
var restaurantArray = [Restaurant]()
var lastDocument: DocumentSnapshot?
var currentLocation: CLLocation?
typealias fetchRestaurantCallback = (_ restaurants: [Restaurant]?, _ message: String?, _ status: FetchRestaurant) -> Void
var restaurantFetched: fetchRestaurantCallback?
var fetchRestaurant: FetchRestaurant?
init(restaurant: [Restaurant]) {
self.restaurant = restaurant
}
func fetchRestaurantCallback (callback: #escaping fetchRestaurantCallback) {
self.restaurantFetched = callback
}
func fetchRestaurants(address: String) {
print("address received: \(address)")
getLocation(from: address) { location in
if let location = location {
self.currentLocation = location
self.queryGenerator(at: location)
} else {
self.restaurantFetched?(nil, nil, .location)
}
}
}
func queryGenerator(at location: CLLocation) {
var query: Query!
if restaurantArray.isEmpty {
query = db.collection("Restaurant_Data").whereField("distributionType", isLessThanOrEqualTo: 2).limit(to: Constants.Mealplan.LOADLIMIT)
} else {
print("last document:\(String(describing: lastDocument?.documentID))")
query = db.collection("Restaurant_Data").whereField("distributionType", isLessThanOrEqualTo: 2).start(afterDocument: lastDocument!).limit(to: Constants.Mealplan.LOADLIMIT)
}
batchFetch(query: query)
}
func batchFetch(query: Query) {
query.getDocuments { (querySnapshot, error) in
if let error = error {
self.restaurantFetched?(nil, error.localizedDescription, .error)
} else if querySnapshot!.isEmpty {
self.restaurantFetched?(nil, nil, .end)
} else if !querySnapshot!.isEmpty {
let queriedRestaurants = querySnapshot?.documents.compactMap { querySnapshot -> Restaurant? in
return try? querySnapshot.data(as: Restaurant.self)
}
guard let restaurants = queriedRestaurants,
let currentLocation = self.currentLocation else {
self.restaurantFetched?(nil, nil, .end)
return }
self.restaurantArray.append(contentsOf: self.applicableRestaurants(allQueriedRestaurants: restaurants, location: currentLocation))
DispatchQueue.main.asyncAfter(deadline: .now(), execute: {
self.restaurantFetched?(self.restaurantArray, nil, .success)
})
self.lastDocument = querySnapshot!.documents.last
}
}
}
func getLocation(from address: String, completionHandler: #escaping (_ location: CLLocation?) -> Void) {
let geocoder = CLGeocoder()
geocoder.geocodeAddressString(address) { (placemarks, error) in
guard let placemarks = placemarks,
let location = placemarks.first?.location else {
completionHandler(nil)
return
}
completionHandler(location)
}
}
}
And in the VC viewDidLoad:
var fetchMore = false
var reachedEnd = false
let leadingScreensForBatching: CGFloat = 5.0
var searchController = UISearchController(searchResultsController: nil)
var currentAddress : String?
var listViewModel = ListViewModel(restaurant: [Restaurant]())
override func viewDidLoad() {
super.viewDidLoad()
listViewModel.fetchRestaurantCallback { (restaurants, error, result) in
switch result {
case .success :
self.loadingShimmer.stopShimmering()
self.loadingShimmer.removeFromSuperview()
guard let fetchedRestaurants = restaurants else { return }
self.restaurantArray.append(contentsOf: fetchedRestaurants)
self.tableView.reloadData()
self.fetchMore = false
case .location :
self.showAlert(alertTitle: "No businesses nearby", message: "Try going back and changing the address")
case .error :
guard let error = error else { return }
self.showAlert(alertTitle: "Error", message: error)
case .end :
self.fetchMore = false
self.reachedEnd = true
}
}
if let currentAddress = currentAddress {
listViewModel.fetchRestaurants(address: currentAddress)
}
}
I would really appreciate links or resources for implementing MVVM in Swift for a Firestore back-end. I'm coming up short on searches here and on Google. Even tried medium.
EDIT
class ListViewController: UITableViewController {
lazy var loadingShimmer: UIImageView = {
let image = UIImage(named: "shimmer_background")
let imageview = UIImageView(image: image)
imageview.contentMode = .top
imageview.translatesAutoresizingMaskIntoConstraints = false
return imageview
}()
var restaurantArray = [Restaurant]()
var planDictionary = [String: Any]()
var fetchMore = false
var reachedEnd = false
let leadingScreensForBatching: CGFloat = 5.0
var searchController = UISearchController(searchResultsController: nil)
var currentAddress : String?
var listViewModel = ListViewModel(restaurant: [Restaurant]())
override func viewDidLoad() {
super.viewDidLoad()
setupTable()
}
override func viewWillAppear(_ animated: Bool) {
clearsSelectionOnViewWillAppear = false
}
func setupTable() {
navigationItem.backBarButtonItem = UIBarButtonItem(title: "Restaurant", style: .plain, target: nil, action: nil)
tableView.register(RestaurantCell.self, forCellReuseIdentifier: "Cell")
tableView.delegate = self
tableView.dataSource = self
let navigationBarHeight: CGFloat = self.navigationController!.navigationBar.frame.height
tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: -navigationBarHeight, right: 0)
tableView.separatorStyle = .none
tableView.showsVerticalScrollIndicator = false
tableView.addSubview(loadingShimmer)
loadingShimmer.topAnchor.constraint(equalTo: tableView.safeAreaLayoutGuide.topAnchor).isActive = true
loadingShimmer.leadingAnchor.constraint(equalTo: tableView.leadingAnchor).isActive = true
loadingShimmer.trailingAnchor.constraint(equalTo: tableView.trailingAnchor).isActive = true
loadingShimmer.startShimmering()
initialSetup()
}
func initialSetup() {
let addressOne = planDictionary["addressOne"] as! String + ", "
let city = planDictionary["city"] as! String + ", "
let postalCode = planDictionary["postalCode"] as! String
currentAddress = addressOne + city + postalCode
setupSearch()
listViewModel.fetchRestaurantCallback { (restaurants, error, result) in
switch result {
case .success :
self.loadingShimmer.stopShimmering()
self.loadingShimmer.removeFromSuperview()
guard let fetchedRestaurants = restaurants else { return }
self.restaurantArray.append(contentsOf: fetchedRestaurants)
self.tableView.reloadData()
self.fetchMore = false
case .location :
self.showAlert(alertTitle: "No businesses nearby", message: "Try going back and changing the address")
case .error :
guard let error = error else { return }
self.showAlert(alertTitle: "Error", message: error)
case .end :
self.fetchMore = false
self.reachedEnd = true
}
}
if let currentAddress = currentAddress {
listViewModel.fetchRestaurants(address: currentAddress)
}
}
override func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
let off = scrollView.contentOffset.y
let off1 = scrollView.contentSize.height
if off > off1 - scrollView.frame.height * leadingScreensForBatching {
print("\(fetchMore), \(reachedEnd)")
if !fetchMore && !reachedEnd {
if let address = self.currentAddress {
print("address sent: \(address)")
listViewModel.fetchRestaurants(address: address)
}
}
}
}
}
That you're only getting back 4 results instead of 10 is not due to a faulty query or get-document request—those are coded properly. You're either losing documents when you parse them (some are failing Restaurant initialization), Constants.Mealplan.LOADLIMIT is wrong, or there aren't more than 4 documents in the collection itself that satisfy the query.
That the query is executed 3 times instead of once is also not due to anything in this code—viewDidLoad is only called once and geocodeAddressString only returns once. You're making a fetch request elsewhere that we can't see.
In the batchFetch method, you have a guard that returns out of the function without ever calling its completion handler. This will leave the UI in a state of limbo. I'd recommend always calling the completion handler no matter why the function returns.
You never manage the document cursor. If the get-document return has less documents than the load limit, then nil the last-document cursor. This way, when you attempt to get the next page of documents, guard against a nil cursor and see if there is even more to fetch.
There's no need to pass in an empty array and have your function fill it; simply construct and return an array of results within ListViewModel itself.
We can't see how you trigger pagination. Is it through a scroll delegate when the user reaches the bottom or through a button tap, for example? If it's through a scroll delegate, then I'd disable that for now and see how many returns you get—I suspect one, instead of 3.
What is the particular reason you've ditched MVC for MVVM here? With MVC, you can get pagination up with just a few lines of code. I think MVVM is overkill for iOS applications and would advise against using it unless you have a compelling reason.

fatal error: unexpectedly found nil while unwrapping an Optional value. But I can't find nill value in my statement

I've got an error on the let myValue line:
#IBAction func CAttamaran(_ sender: Any) {
// error happens here
let myValue:NSString = (sender as AnyObject).titleLabel!!.text! as NSString
UserDefaults.standard.set(myValue, forKey:"Shared room")
UserDefaults.standard.synchronize()
if let myOutput2: AnyObject = UserDefaults.standard.object(forKey: "Shared room") as AnyObject? {
// self.appDelegate.propertylabel = "\(myOutput2)" as NSString!
let secondViewController1 = self.storyboard!.instantiateViewController(withIdentifier: "propertyvc")
self.present(secondViewController1, animated: true, completion: nil)
print("property_id = \(myOutput2)")
}
}
This line
let myValue:NSString = (sender as AnyObject).titleLabel!!.text! as NSString
has three unwrappings in it, any one of which could be your problem
sender may not be castable as AnyObject
titleLabel might be nil especially if sender is an Objective-C object that doesn't have a titleLabel property.
titleLabel.text might be nil.
If you want to find out what the problem is, you need to do the unwrappings one at a time e.g.
guard let sender = sender as WhateverTypeYouThinkItShouldBe else { fatalError("sender is the wrong type") }
guard let titleLabel = sender.titleLabel else { fatalError("Title label is nil") }
if let text = titleLabel.text
{
// Do whatever you need to do
}
else
{
// There is no text in the label
}
The line of code you are pointing on, has a lot of issues:
let myValue:NSString = (sender as AnyObject).titleLabel!!.text! as NSString
First and first, force unwrapping like that is evil. It's the biggest source for possible crashes.
Why do you try to cast to AnyObject? That one doesn't have titleLabel property, so this wrong.
Why do you cast the text to NSString? Didn't notice any particular NSString API usage.
if let myOutput2: AnyObject = UserDefaults.standard.object(forKey: "Shared room") as AnyObject? {
You read again the same value from UserDefaults that you just saved.
If you rewrite your code in more Swift friendly way, it would be better:
Here is an example of your re-written function:
#IBAction func CAttamaran(_ sender: UIButton) {
guard let myValue = sender.titleLabel?.text else {
// Handle the edge case here
return
}
UserDefaults.standard.set(myValue, forKey:"Shared room")
UserDefaults.standard.synchronize()
guard let storyboard = self.storyboard else {
// Handle the edge case here
return
}
let secondViewController1 = storyboard.instantiateViewController(withIdentifier: "propertyvc")
self.present(secondViewController1, animated: true, completion: nil)
print("property_id = \(myValue)")
}
Any exclamation mark can cause that exception.
in your case
// problematic line is
let myValue:NSString = (sender as AnyObject).titleLabel!!.text! as
NSString
as we can see there is multiple chance of getting nil value
like
sender may not be castable as AnyObject
titleLabel might be nil
especially if sender is an Objective-C object that doesn't have a
titleLabel property.
titleLabel.text might be nil.
Solution for this is use guard statement like below
#IBAction func CAttamaran(_ sender: UIButton) {
guard let myValue = sender.titleLabel?.text else {
// Handle the edge case here
return
}
guard let storyboard = self.storyboard else {
// Handle the edge case here
return
}
// Rest of code
}

can't generate tableview using rxcoca

I have an app, that shows current weather. Data is downloaded via Alamofire from forecast.io. Result forms a table with forecast. I used simple tableviewdelegate and tableviewDatasource, everything works. But now I wanted to learn some reactive, using rxswift and rxcocoa. After some googling, and tutorial from raywanderlich: I changed my code to:
var week = Variable<[DailyWeather]>([])
override func viewDidLoad() {
super.viewDidLoad()
week.asObservable().subscribe { (e) in
self.generateTable()
}.addDisposableTo(disposeBag)
// tableView.delegate = self
// tableView.dataSource = self
tableView.backgroundColor = UIColor(red:0.81, green:0.81, blue:0.81, alpha:1)
refreshControl.tintColor = UIColor(red:1, green:1, blue:1, alpha:1)
self.tableView.addSubview(self.refreshControl)
manager.delegate = self
manager.requestWhenInUseAuthorization()
updateLocation()
}
func downloadData(_ completion: #escaping DownloadComplete) {
var weatherURL: String {
if pre == "ru" {
return("\(BASE_URL)\(API_KEY)/\(latitudeNew),\(longitudeNew)?units=si&lang=ru")
} else {
return("\(BASE_URL)\(API_KEY)/\(latitudeNew),\(longitudeNew)?units=si")
}
}
print(weatherURL)
if let url = URL(string: weatherURL) {
let request = Alamofire.request(url)
request.validate().responseJSON { response in
switch response.result {
case .success:
self.week.value = []
self.weekly = []
if let data = response.result.value as! [String: AnyObject]! {
self.weatherDict = data["currently"] as! [String: AnyObject]!
self.currentDict = CurrentWeather(weatherDictionary: self.weatherDict)
self.dailyArray = data["daily"]?["data"] as! [[String: AnyObject]]!
for dailyWeather in self.dailyArray {
let daily = DailyWeather(dailyWeatherDict: dailyWeather)
self.weekly.append(daily)
}
for x in 0...7 {
if x == 0 {
} else {
self.week.value.append(self.weekly[x])
}
}
completion()
}
case .failure(let error):
self.showAlert("You are offline", message: "Enable network connection and try again")
print("Alamofire error: \(error)")
}
}
}
}
func generateTable() {
week.asObservable().bindTo(tableView.rx.items(cellIdentifier: "WeatherViewCell", cellType: WeatherViewCell.self)) { (index, weather, cell) in
cell.configureCell(daily: weather, index: index)
}.addDisposableTo(disposeBag)
}
But I receive this fatal error:
fatal error: Failure converting from Optional(<UIView: 0x7facad60f620; frame = (0 0; 414 736); autoresize = W+H; layer = <CALayer: 0x60800002e520>>) to UITableViewDataSource: file /Users/ruslansabirov/Desktop/swift/Havo4/Pods/RxCocoa/RxCocoa/RxCocoa.swift, line 146
(lldb)
Pls, help, what I'm doing wrong?
Check table view delegate and datasource methods.
if tableview delegate is set in your code then you must implement the delegate method of tableview while using RXSwift otherwise application got crash.
you only need to call func generateTable() once. The tableview will update automatically. As it is a data bindingoverride
func viewDidLoad() {
super.viewDidLoad()
self.generateTable()
tableView.backgroundColor = UIColor(red:0.81, green:0.81, blue:0.81, alpha:1)
refreshControl.tintColor = UIColor(red:1, green:1, blue:1, alpha:1)
self.tableView.addSubview(self.refreshControl)
manager.delegate = self
manager.requestWhenInUseAuthorization()
updateLocation()
}

Why does Xcode tell me 'stringValue' or 'text' cannot be used on type 'NSSearchField'?

I'm actually Swift Developer with main emphasis in UIKit and not AppKit or Cocoa.
So Xcode tells me NSSearchField.stringValue? nor NSSearchField.text? is working, is that true? and if so, what should I use instead?
extension MapViewController: NSSearchFieldDelegate {
override func searchFieldAction(sender: NSSearchField)
defer {
}
guard let text = NSSearchField.stringValue?.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
where !text.isEmpty else { return }
let request = MKLocalSearchRequest()
request.naturalLanguageQuery = text
request.region = mapView.region
let search = MKLocalSearch(request: request)
search.startWithCompletionHandler { (response, error) in
guard let item = response?.mapItems.first else { return }
let annotation = item.placemark
self.mapView.addAnnotation(annotation)
self.mapView.showAnnotations([annotation], animated: true)
self.mapView.selectAnnotation(annotation, animated: true)
}
}
NSSearchField is the class. sender is an instance of NSSearchField.
You want to use the instance of the class sender.stringValue

Swift iOS google Map, path to coordinate

I am trying to create a function in my app that will guide the user to a marker I have created.
This is the code I am using, it works great, It gets the users current location and show it on the map. But how can I get a directions to a marker?
Any awnser will be helpful
class Karta: UIViewController, CLLocationManagerDelegate {
#IBOutlet var mapView: GMSMapView!
let locationManager = CLLocationManager()
override func viewDidLoad() {
super.viewDidLoad()
//allow app to track user
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()
//set out a marker on the map
var marker = GMSMarker()
marker.position = CLLocationCoordinate2DMake(56.675907, 12.858798)
marker.appearAnimation = kGMSMarkerAnimationPop
marker.icon = UIImage(named: "flag_icon")
marker.map = mapView
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "Types Segue" {
let navigationController = segue.destinationViewController as UINavigationController
}
}
func locationManager(manager: CLLocationManager!, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
//If map is being used
if status == .AuthorizedWhenInUse {
var myLocation = mapView
locationManager.startUpdatingLocation()
mapView.myLocationEnabled = true
mapView.settings.myLocationButton = true
}
}
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
if let location = locations.first as? CLLocation {
mapView.camera = GMSCameraPosition(target: location.coordinate, zoom: 15, bearing: 0, viewingAngle: 0)
locationManager.stopUpdatingLocation()
}
}
}
So i recently just solved this issue, here is my Swift 3 implementation using the latest version of Alamofire (4.3)
func fetchMapData() {
let directionURL = "https://maps.googleapis.com/maps/api/directions/json?" +
"origin=\(originAddressLat),\(originAddressLng)&destination=\(destinationAddressLat),\(destinationAddressLong)&" +
"key=YOUROWNSERVERKEY"
Alamofire.request(directionURL).responseJSON
{ response in
if let JSON = response.result.value {
let mapResponse: [String: AnyObject] = JSON as! [String : AnyObject]
let routesArray = (mapResponse["routes"] as? Array) ?? []
let routes = (routesArray.first as? Dictionary<String, AnyObject>) ?? [:]
let overviewPolyline = (routes["overview_polyline"] as? Dictionary<String,AnyObject>) ?? [:]
let polypoints = (overviewPolyline["points"] as? String) ?? ""
let line = polypoints
self.addPolyLine(encodedString: line)
}
}
}
func addPolyLine(encodedString: String) {
let path = GMSMutablePath(fromEncodedPath: encodedString)
let polyline = GMSPolyline(path: path)
polyline.strokeWidth = 5
polyline.strokeColor = .blue
polyline.map = whateverYourMapViewObjectIsCalled
}
Disclaimer:Swift 2
func addOverlayToMapView(){
let directionURL = "https://maps.googleapis.com/maps/api/directions/json?origin=\(srcLocation.coordinate.latitude),\(srcLocation.coordinate.longitude)&destination=\(destLocation.coordinate.latitude),\(destLocation.coordinate.longitude)&key=Your Server Key"
Alamofire.request(.GET, directionURL, parameters: nil).responseJSON { response in
switch response.result {
case .Success(let data):
let json = JSON(data)
print(json)
let errornum = json["error"]
if (errornum == true){
}else{
let routes = json["routes"].array
if routes != nil{
let overViewPolyLine = routes![0]["overview_polyline"]["points"].string
print(overViewPolyLine)
if overViewPolyLine != nil{
self.addPolyLineWithEncodedStringInMap(overViewPolyLine!)
}
}
}
case .Failure(let error):
print("Request failed with error: \(error)")
}
}
}
Using the points, we now create the Path from two points using fromEncodedPath
func addPolyLineWithEncodedStringInMap(encodedString: String) {
let path = GMSMutablePath(fromEncodedPath: encodedString)
let polyLine = GMSPolyline(path: path)
polyLine.strokeWidth = 5
polyLine.strokeColor = UIColor.yellowColor()
polyLine.map = mapView
}
Unlike Apple's MapKit, the Google Maps SDK for iOS does not natively include a way to perform route calculations.
Instead, you need to use the Google Directions API: https://developers.google.com/maps/documentation/directions/. It is an HTTP-only API, and Google does not provide any SDK as of today, but you can easily write your own wrapper yourself, or choose one of the many options available on Github:
https://github.com/sudeepjaiswal/GoogleDirections
https://github.com/sebk/GoogleDirections
https://github.com/iamamused/iOS-DirectionKit
https://github.com/marciniwanicki/OCGoogleDirectionsAPI
and many others...
Its Very Simple
if you added a Google map SDK in your iOS Project and if you want to implement get google map direction lines between two different directions i have made as demo code in simple way understand using swift 2.3 try it, modify it and use it.!!!
Note: Dont Forgot to change your lat long you want and API Key (You may Use API key with None Restrictions on Google API Manager Credential Section)
func callWebService(){
let url = NSURL(string: "https://maps.googleapis.com/maps/api/directions/json?origin=\(18.5235),\(73.7184)&destination=\(18.7603),\(73.8630)&key=AIzaSyDxSgGQX6jrn4iq6dyIWAKEOTneZ3Z8PtU")
let request = NSURLRequest(URL: url!)
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let task = session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
// notice that I can omit the types of data, response and error
do{
if let jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as? NSDictionary {
//print(jsonResult)
let routes = jsonResult.valueForKey("routes")
//print(routes)
let overViewPolyLine = routes![0]["overview_polyline"]!!["points"] as! String
print(overViewPolyLine)
if overViewPolyLine != ""{
//Call on Main Thread
dispatch_async(dispatch_get_main_queue()) {
self.addPolyLineWithEncodedStringInMap(overViewPolyLine)
}
}
}
}
catch{
print("Somthing wrong")
}
});
// do whatever you need with the task e.g. run
task.resume()
}
func addPolyLineWithEncodedStringInMap(encodedString: String) {
let camera = GMSCameraPosition.cameraWithLatitude(18.5204, longitude: 73.8567, zoom: 10.0)
let mapView = GMSMapView.mapWithFrame(CGRect.zero, camera: camera)
mapView.myLocationEnabled = true
let path = GMSMutablePath(fromEncodedPath: encodedString)
let polyLine = GMSPolyline(path: path)
polyLine.strokeWidth = 5
polyLine.strokeColor = UIColor.yellowColor()
polyLine.map = mapView
let smarker = GMSMarker()
smarker.position = CLLocationCoordinate2D(latitude: 18.5235, longitude: 73.7184)
smarker.title = "Lavale"
smarker.snippet = "Maharshtra"
smarker.map = mapView
let dmarker = GMSMarker()
dmarker.position = CLLocationCoordinate2D(latitude: 18.7603, longitude: 73.8630)
dmarker.title = "Chakan"
dmarker.snippet = "Maharshtra"
dmarker.map = mapView
view = mapView
}