Get image URL for a given Google Place ID in iOS - ios

I have a GMSPlace object and I want to get the image URL for that place but the Google Places SDK doesn't seem to allow for that.
loadImageForMetadata only returns an object of type UIImage. I don't need the image just the URL.
How do I find the image url for a given Google Place ID? Here's a class I've written to help me work with the Google Places SDK:
import Foundation
import GooglePlaces
class GoogleImageService: NSObject {
class func loadFirstPhotoForPlace(placeID: String) {
GMSPlacesClient.shared().lookUpPhotos(forPlaceID: placeID) { (photos, error) -> Void in
if let error = error {
print("Error: \(error.localizedDescription)")
}
if let firstPhoto = photos?.results[0] {
self.loadImageForMetadata(photoMetadata: firstPhoto, placeID: placeID)
}
}
}
class func loadImageForMetadata(photoMetadata: GMSPlacePhotoMetadata, placeID: String) {
GMSPlacesClient.shared().loadPlacePhoto(photoMetadata, callback: { (photo, error) -> Void in
if let error = error {
print("Error: \(error.localizedDescription)")
}
// photo parameter in the closure is an UIImage, not helpful
})
}
}

Related

New Apple Vision API VNTranslationalImageRegistrationRequest

I am trying to play with the Image Alignment Analysis part of the new Vision API but i am struggling with the initialisation of VNTranslationalImageRegistrationRequest. My code is as follows:
import UIKit
import Vision
class ImageTranslation {
var sourceImage: UIImage!
lazy var imageTranslationRequest: VNTranslationalImageRegistrationRequest = {
//This line fails let translationRequest = VNTranslationalImageRegistrationRequest(targetedCGImage: sourceImage.cgImage, completionHandler: self.handleImageTranslationRequest)
return translationRequest
}()
func handleImageTranslationRequest(request: VNRequest, error: Error?) {
guard let observations = request.results as? [VNImageTranslationAlignmentObservation]
else { print("unexpected result type from VNDetectRectanglesRequest")
return
}
guard observations.first != nil else {
return
}
DispatchQueue.main.async {
observations.forEach { observation in
let transform = observation.alignmentTransform
print(transform)
}
}
}
}
But on the marked line above i keep getting the following error and am unsure how to fix it. Instance member 'sourceImage' cannot be used on type 'ImageTranslation'
Can anyone point me in the right direction? Thanks
An example how to use VNTranslationalImageRegistrationRequest on two images to compute the translation between the two images:
import UIKit
import Vision
class ImageTranslation {
func translateImages(for referenceImage: UIImage,
floatingImage: UIImage,
completion: #escaping (_ alignmentTransform : CGAffineTransform?) -> Void) {
let translationRequest = VNTranslationalImageRegistrationRequest(targetedCGImage: floatingImage.cgImage!) { (request: VNRequest, error: Error?) in
completion((request.results?.first as? VNImageTranslationAlignmentObservation)?.alignmentTransform)
}
let vnImage = VNSequenceRequestHandler()
try? vnImage.perform([translationRequest], on: referenceImage.cgImage!)
}
}

How to handle SignalProducer with ReactiveSwift and Firebase asynchronous method calls?

I am working on an iOS App with Swift 3 using ReactiveSwift 1.1.1, the MVVM + Flow Coordinator pattern and Firebase as a backend. I only recently started to adapt to FRP and I am still trying to figure out how to integrate new functionalities into my existing code base.
For instance, my model uses a asynchronous method from Firebase to download thumbnails from the web and I want to provide a SignalProducer<Content, NoError> to subscribe from my ViewModel classes and observe, if thumbnails have been downloaded, which then updates the UI.
// field to be used from the view-models to observe
public let thumbnailContentSignalProducer = SignalProducer<Content, NoError> { (observer, disposable) in
// TODO: send next content via completion below
}
// thumbnail download method
public func findThumbnail(bucketId: String, contentId: String) {
guard let userId = userService.getCurrentUserId() else {
debugPring("Error id")
return
}
let ref = self.storageThumbnail.reference()
let contentRef = ref
.child(userId)
.child(bucketId)
.child(FirebaseConstants.pathImages)
.child("\(contentId).jpg")
contentRef.data(withMaxSize: 1 * 1024 * 1024, completion: { (data, error) in
guard let data = data else {
debugPrint("Error download")
return
}
let content = Image(data: data)
content.id = contentId
content.userId = userId
content.bucketId = bucketId
// TODO: emit signal with content
// How to send the content via the SignalProducer above?
})
}
I have also tried something similar with Signal<Content, NoError>, whereas I used the Signal<Content, NoError>.pipe() method to receive a (observer, disposable) tuple and I saved the observer as a private global field to access it form the Firebase callback.
Questions:
Is this the right approach or am I missing something?
How do I emit the content object on completion?
UPDATE:
After some hours of pain, I found out how to design the SingalProducer to emit signals and to subscribe from the ViewModels.
Maybe the following code snippet will help also others:
// model protocol
import ReactiveSwift
import enum Result.NoError
public protocol ContentService {
func findThumbnail(bucketId: String, contentId: String)
var thumbnailContentProducer: SignalProducer<Content, NoError> { get }
}
// model implementation using firebase
import Firebase
import FirebaseStorage
import ReactiveSwift
public class FirebaseContentService: ContentService {
// other fields, etc.
// ...
private var thumbnailContentObserver: Observer<Content, NoError>?
private var thumbnailContentSignalProducer: SignalProducer<Content, NoError>?
var thumbnailContentProducer: SignalProducer<Content, NoError> {
return thumbnailContentSignalProducer!
}
init() {
thumbnailContentSignalProducer = SignalProducer<Content, NoError> { (observer, disposable) in
self.thumbnailContentObserver = observer
}
}
func findThumbnail(bucketId: String, contentId: String) {
guard let userId = userService.getCurrentUserId() else {
// TODO handle error
return
}
let ref = self.storageThumbnail.reference()
let contentRef = ref
.child(userId)
.child(bucketId)
.child(FirebaseConstants.pathImages)
.child("\(contentId).jpg")
contentRef.data(withMaxSize: 1 * 1024 * 1024, completion: { (data, error) in
guard let data = data else {
// TODO handle error
return
}
let content = Image(data: data)
content.id = contentId
content.userId = userId
content.bucketId = bucketId
// emit signal
self.thumbnailContentObserver?.send(value: content)
})
}
}
// usage from a ViewModel
contentService.thumbnailContentProducer
.startWithValues { content in
self.contents.append(content)
}
Maybe someone can verify the code above and say that this is the right way to do it.
I think you were on the right path when you were looking at using Signal with pipe. The key point is that you need to create a new SignalProducer for each thumbnail request, and you need a way to combine all of those requests into one resulting signal. I was thinking something like this (note this is untested code, but it should get the idea across):
class FirebaseContentService {
// userService and storageThumbnail defined here
}
extension FirebaseContentService: ReactiveExtensionsProvider { }
extension Reactive where Base: FirebaseContentService {
private func getThumbnailContentSignalProducer(bucketId: String, contentId: String) -> SignalProducer<Content, ContentError> {
return SignalProducer<Content, ContentError> { (observer, disposable) in
guard let userId = self.base.userService.getCurrentUserId() else {
observer.send(error: ContentError.invalidUserLogin)
return
}
let ref = self.base.storageThumbnail.reference()
let contentRef = ref
.child(userId)
.child(bucketId)
.child(FirebaseConstants.pathImages)
.child("\(contentId).jpg")
contentRef.data(withMaxSize: 1 * 1024 * 1024, completion: { (data, error) in
guard let data = data else {
observer.send(error: ContentError.contentNotFound)
return
}
let content = Image(data: data)
content.id = contentId
content.userId = userId
content.bucketId = bucketId
observer.send(value: content)
observer.sendCompleted()
})
}
}
}
class ThumbnailProvider {
public let thumbnailSignal: Signal<Content, NoError>
private let input: Observer<(bucketId: String, contentId: String), NoError>
init(contentService: FirebaseContentService) {
let (signal, observer) = Signal<(bucketId: String, contentId: String), NoError>.pipe()
self.input = observer
self.thumbnailSignal = signal
.flatMap(.merge) { param in
return contentService.reactive.getThumbnailContentSignalProducer(bucketId: param.bucketId, contentId: param.contentId)
.flatMapError { error in
debugPrint("Error download")
return SignalProducer.empty
}
}
}
public func findThumbnail(bucketId: String, contentId: String) {
input.send(value: (bucketId: bucketId, contentId: contentId))
}
}
Using ReactiveExtensionsProvider like this is the idiomatic way of adding reactive APIs to existing functionality via a reactive property.
The actual requesting code is confined to getThumbnailContentSignalProducer which creates a SignalProducer for each request. Note that errors are passed along here, and the handling and conversion to NoError happens later.
findThumbnails just takes a bucketId and contentId and sends it through the input observable.
The construction of thumbnailSignal in init is where the magic happens. Each input, which is a tuple containing a bucketId and contentId, is converted into a request via flatMap. Note that the .merge strategy means the thumbnails are sent as soon as possible in whatever order the requests complete. You can use .concat if you want to ensure that the thumbnails are returned in the same order they were requested.
The flatMapError is where the potential errors get handled. In this case it's just printing "Error download" and doing nothing else.

Use of undeclared type in Swift 3

I have the following class to return a list of NOAA weather observation stations. I am using it to learn how to deal with XML. However, I am getting a "Use of undeclared type 'wxObservationStations'" as an error at func returnWxStation() -> (wxObservationStations). I am using SWXMLHash to deserialize the XML, but I don't think that is my problem (though I am just learning, so it may be).
class WxObservationStations {
let wxObserStationsURL = URL(string: "http://w1.weather.gov/xml/current_obs/index.xml")
struct wxStation: XMLIndexerDeserializable {
let stationName: String
let stationState: String
let latitude: Double
let longitude: Double
static func deserialize(_ node: XMLIndexer) throws -> wxStation {
return try wxStation(
stationName: node["station_name"].value(),
stationState: node["state"].value(),
latitude: node["latitude"].value(),
longitude: node["longitude"].value()
)
}
}
public var wxObservationStations: [wxStation] = []
private func getStationNamesAndLocations(url: URL, completion:#escaping (XMLIndexer) -> ()) {
Alamofire.request(url).responseJSON { response in
// print(response) // To check XML data in debug window.
let wxStationList = SWXMLHash.parse(response.data!)
print(wxStationList)
completion(wxStationList)
}
}
//The error is here:
func returnWxStation() -> (wxObservationStations) {
getStationNamesAndLocations(url: wxObserStationsURL!, completion: { serverResponse in
do {
self.wxObservationStations = try serverResponse["wx_station_index"]["station"].value()
} catch {
}
})
return self.wxObservationStations
}
}
Any thoughts? The variable is declared in the class, and I would like to use it to send the data back to the requesting object. Thanks in advance.
The wxObservationStations is not a type, so it doesn't make sense to say
func returnWxStation() -> (wxObservationStations) { ... }
You're returning self.wxObservationStations, which is of type [wxStation]. So the method declaration should be
func returnWxStation() -> [wxStation] { ... }
By the way, your life will be much easier if you stick with Cocoa naming conventions, namely types should start with upper case letters. So rather than the wxStation type, I'd suggest WxStation.
Your following method will not achieve what you want:
func returnWxStation() -> [wxStation] {
getStationNamesAndLocations(url: wxObserStationsURL!, completion: { serverResponse in
do {
self.wxObservationStations = try serverResponse["wx_station_index"]["station"].value()
} catch {
}
})
return self.wxObservationStations
}
The method getStationNamesAndLocations runs asynchronously and your self.wxObservationStations will not be populated by the time that returnWxStation actually returns.
The entire purpose of the getStationNamesAndLocations method is to provide you a nice asynchronous method with completion handler. I would excise returnWxStation from your code entirely. Or do something like:
func returnWxStation(completionHandler: ([wxStation]?) -> Void) {
getStationNamesAndLocations(url: wxObserStationsURL!) { serverResponse in
do {
let stations = try serverResponse["wx_station_index"]["station"].value()
completionHandler(stations)
} catch {
completionHandler(nil)
}
}
}
And you'd use it like so:
returnWxStation() { stations in
guard let stations = stations else {
// handle error here
return
}
// use `stations` here
}
// but not here

How to get GMSPlace for a CLLocationCoordinate2D?

I have a location coordinate in the form of a CLLocationCoordinate2D. How do I get its equivalent GMSPlace object using the Google Maps SDK?
This seems like it should be a very simple task, but I couldn't find anything in Google's documentation or on Stack Overflow.
I am working on a similar issue, and I haven't found the exact solution but these alternatives may work depending on your situation. If you are okay with having a GMSAddress instead of a GMSPlace, you may use a GMSGeocoder with a call to reverseGeocodeCoordinate as seen in option two below.
Two options if you're trying to get the user's current location:
Use Google Maps current location to get a GMSPlace. This is pretty simple and solves your problem if you are okay with only resorting to actual places. The problem with this is that I couldn't figure out how to get all addresses (as opposed to businesses). You can see the documentation here.
In viewDidLoad:
let placesClient = GMSPlacesClient()
When you want to get the current place:
placesClient?.currentPlaceWithCallback({ (placeLikelihoods, error) -> Void in
if error != nil {
// Handle error in some way.
}
if let placeLikelihood = placeLikelihoods?.likelihoods.first {
let place = placeLikelihood.place
// Do what you want with the returned GMSPlace.
}
})
Use OneShotLocationManager to get the CLLocationCoordinate2D and turn it into a GMSAddress. You will have to replace the _didComplete function with the code below to return a GMSAddress instead of a CLLocationCoordinate2D.
private func _didComplete(location: CLLocation?, error: NSError?) {
locationManager?.stopUpdatingLocation()
if let location = location {
GMSGeocoder().reverseGeocodeCoordinate(location.coordinate, completionHandler: {
[unowned self] (response, error) -> Void in
if error != nil || response == nil || response!.firstResult() == nil {
self.didComplete?(location: nil,
error: NSError(domain: self.classForCoder.description(),
code: LocationManagerErrors.InvalidLocation.rawValue,
userInfo: nil))
} else {
self.didComplete?(location: response!.firstResult(), error: error)
}
})
} else {
self.didComplete?(location: nil, error: error)
}
locationManager?.delegate = nil
locationManager = nil
}
Someone posted on here a convenient wrapper to extract fields from the GMSAddressComponents that you may find useful when dealing with this API. This makes it easy because then when you want to access the city all you have to do is place.addressComponents?.city as an example.
extension CollectionType where Generator.Element == GMSAddressComponent {
var streetAddress: String? {
return "\(valueForKey("street_number")) \(valueForKey(kGMSPlaceTypeRoute))"
}
var city: String? {
return valueForKey(kGMSPlaceTypeLocality)
}
var state: String? {
return valueForKey(kGMSPlaceTypeAdministrativeAreaLevel1)
}
var zipCode: String? {
return valueForKey(kGMSPlaceTypePostalCode)
}
var country: String? {
return valueForKey(kGMSPlaceTypeCountry)
}
func valueForKey(key: String) -> String? {
return filter { $0.type == key }.first?.name
}
}

Parse Swift - Subclassing PFObject - setting property causing app to hang with no error message

I am subclassing a Parse object called "Item". Everything that follows is based off the Parse documentation: https://parse.com/docs/ios/guide#objects-subclasses
I am trying to get the object from Parse, and simply add a local property called thumbnailImage which is of type UIImage.
This is my bridging header:
#ifndef My_Bridging_Header_h
#define My_Bridging_Header_h
#endif /* My_Bridging_Header_h */
#import <Parse/PFObject+Subclass.h>
This is my "Item.swift" file:
import UIKit
import Parse
class Item : PFObject, PFSubclassing {
override class func initialize() {
struct Static {
static var onceToken : dispatch_once_t = 0;
}
dispatch_once(&Static.onceToken) {
self.registerSubclass()
}
}
#NSManaged var thumbnailImage: UIImage
static func parseClassName() -> String {
return "Item"
}
}
Finally, in my viewController, the code hangs after the "hello world" line:
let query = PFQuery(className: "Item")
query.whereKey("expired", equalTo: false)
query.findObjectsInBackgroundWithBlock({ (results, error) -> Void in
if error != nil {
print(error)
}
else {
self.itemsToDisplay.removeAll()
print(results!.count)
self.itemsToDisplay = results as! [Item]
for item in self.itemsToDisplay {
let downloadedItemThumbnailImage = item["thumbnailImage"] as! PFFile
do {
let downloadedItemThumbnailImageData = try UIImage(data: downloadedItemThumbnailImage.getData())
print("hello world")
//the problem line is below
item.thumbnailImage = downloadedItemThumbnailImageData!
print("now I am here!")
}
catch {
print("error with downloaded image data")
}
}
itemsToDisplay is an array of [Item], which is my PFObject subclass.
Xcode doesn't give me any error message, though eventually I do get a memory warning and have to quit the app. Not sure what is happening.
Why doesn't this work: item.thumbnailImage = downloadedItemThumbnailImageData!
How do I set this local property on the subclassed PFObject?
Lastly, I have registered my subclass at the top of my AppDelegate.swift file:
// Parse Register Subclasses
Item.registerSubclass()
When subclassing PFObject, you do not want to have a UIImage directly as a property. Instead, make it a PFFile. This allows you to download the file, and then get the image data.
If you want to do this all in the same class, you could download the file and then set the property without using #NSManaged, however, this probably wouldn't offer any benefit, and keeps the image in memory as long as the object is.
If you want to use image into the subclass in swift using Parse SDK then you can use this solution.
import Parse
class Person: PFObject, PFSubclassing{
#NSManaged var name: String
#NSManaged var age: Int
#NSManaged var imageFile: PFFileObject!
class func parseClassName() -> String {
return "Person"
}
}
//By writing this function you can add your data to the server with image data
func addData(){
let person = Person()
person.name = "Muhammad ABBAS"
person.age = 21
let image = #imageLiteral(resourceName: "IMAGENAME") // Image litral
// if your image is .jpeg or .jpg then write this
person.imageFile = PFFileObject(data: image.jpegData(compressionQuality: 100)!)
person.saveInBackground { success, error in
if success{
print("Successfully Data Upload")
}else{
print("Not uploaded the data")
}
}
}
if your image is .png then write this
person.imageFile = PFFileObject(data: image.pngData()!)
instead of
person.imageFile = PFFileObject(data: image.jpegData(compressionQuality: 100)!)

Resources