Best Swift way to write Dictionary of String to Country - swift

I want my Swift class FinderForCountry to find the Country with the specified name.
The code should also remember any countries (by name) that have already been found.
My existing Swift class has a [String: Country] variable, and a method findCountry() that takes both a String and a function that takes a Country.
public
class FinderForCountry {
private
var mapOfStringToCountry = [String: Country]()
public
func findCountry(from string: String, _ functionThatTakesCountry: #escaping (Country) -> Void) {
if let country = mapOfStringToCountry[string] {
functionThatTakesCountry(country)
} else {
DispatchQueue.main.async {
let country = Country(string)
self.mapOfStringToCountry[string] = country
functionThatTakesCountry(country)
}
}
}
}
Is this the best way to write the code, or is there a better way? For example:
public
class FinderForCountry {
private
var mapOfStringToCountry = [String: Country]()
public
func findCountry(from string: String, _ functionThatTakesCountry: #escaping (Country) -> Void) {
DispatchQueue.main.async {
if let country = self.mapOfStringToCountry[string] {
functionThatTakesCountry(country)
} else {
let country = Country(string)
self.mapOfStringToCountry[string] = country
functionThatTakesCountry(country)
}
}
}
}
Many thanks.

Yes, there is indeed a better way.
The escaping completion handler and dispatching the code to the main thread make no sense as there is nothing which is executed asynchronously.
Just return the country.
public class FinderForCountry {
private var mapOfStringToCountry = [String: Country]()
public func findCountry(from string: String) -> Country
{
if let country = self.mapOfStringToCountry[string] {
return country
} else {
let country = Country(string)
self.mapOfStringToCountry[string] = country
return country
}
}
}

Related

Getting "Must have a uuid if no _objectID" exception when inserting object into dictionary

I'm writing a unit-test to a class that uses PHAsset type. I mocked it as below:
class PHAssetMock: PHAsset {
let date: Date
let uuid: UUID
init(dateStr: String) {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM-dd-yyyy HH:mm"
self.date = dateFormatter.date(from: dateStr)!
self.uuid = UUID()
}
override var creationDate: Date? {
return date
}
override var hash: Int {
let hash = Int(self.date.timeIntervalSinceNow)
return hash
}
static func ==(lhs: PHAsseMock, rhs: PHAsseMock) -> Bool {
return lhs.date.timeIntervalSinceNow == rhs.date.timeIntervalSinceNow
}
}
When a function that uses mocked objects tries to insert it in a dictionary I'm getting an exception:
func foo(assets: [PHAsset]) {
var label: [T: String]()
for asset in assets {
label[asset] = "undefined" // Exception: "NSInternalInconsistencyException", "Must have a uuid if no _objectID"
}
}
When debugging, the override hash var is being called.
I had the same issue with the PHAsset when unit testing Photos framework. Overriding isEqual function helped to get rid of the exception.
class Mock : PHAsset {
let _localIdentifier: String = UUID().uuidString
let _hash: Int = UUID().hashValue
override var localIdentifier: String {
return _localIdentifier
}
override var hash: Int {
return _hash
}
override func isEqual(_ object: Any?) -> Bool {
guard let object = object as? Mock else {
return false
}
return self.localIdentifier == object.localIdentifier
}
}

Unable to infer closure type in the current context in APIClient

I'm trying to migrate code from Swift 3.2 to Swift 4.2. When put in Xcode 10.1 I get this error.'Unable to infer closure type in the current context '. This is using YALAPIClient. Please help.
Unable to infer closure type in the current context
This I found on stack overflow. But I'm not using any try method here.Please help.
private func presentIndustrySearch() {
let dataProvider = RequestDataProvider { return IndustriesRequest() } /*error comes here*/
}
public class RequestDataProvider<Representation, Request>: DataProvider, NetworkClientInjectable
where
Request: SerializeableAPIRequest,
Request.Parser.Representation == [Representation]
{
public typealias Item = Representation
public typealias RequestConstructor = () -> Request
public private(set) var data = [Item]()
private let requestConstructor: RequestConstructor
public init(_ requestConstructor: #escaping RequestConstructor) {
self.requestConstructor = requestConstructor
}
public func loadData(before: () -> Void, after: #escaping (Error?) -> Void) {
let request: Request = self.requestConstructor()
before()
networkClient.execute(request: request, parser: request.parser, completion: { [weak self] task in
guard let `self` = self else { return }
if let data = task.value, data.count != 0 {
self.data.append(contentsOf: data)
after(nil)
} else if let error = task.error {
after(error as NSError)
} else {
let error = NSError.reachedEndOfPage()
after(error)
}
})
}
}
public protocol SerializeableAPIRequest: APIRequest {
associatedtype Parser: ResponseParser
var parser: Parser { get }
}
public struct IndustriesRequest: SerializeableAPIRequest, TokenizedAPIRequest, StubAPIRequest {
public private(set) var method = APIRequestMethod.get
public private(set) var path = "industries"
public private(set) var parser = KeyPathMappableArrayParser<[Industry]>(keyPath: "data")
public private(set) var parameters: [String: String]? = [:]
public private(set) var authenticationTokenRequired = true
public init(value: String = "") {
parameters!["term"] = value
}
}

How to overcome the error of "Generic parameter 'T' is not used in function signature"?

I'm trying to convert the following to be generic.
extension RLMOrganization: DataProvider {
func getLastSyncToken() -> String {
let lastUpdated: RLMOrganization? = self.findAll(sortedBy: "syncToken").last
if let syncToken = lastUpdated?.syncToken {
return syncToken
} else {
return "00000000000000000000000000000000"
}
}
}
And have tried this:
protocol DataProvider: DatabaseLayer {
associatedtype T: Object
func findAll<T: Object>(sortedBy key: String) -> [T]
}
extension DataProvider {
func findAll<T: Object>(sortedBy key: String) -> [T] {
let database = self.getDatabase()
if let allObjects = database?.objects(T.self) {
let results = allObjects.sorted(byKeyPath: key, ascending: true)
return Array(results)
}
return []
}
func getLastSyncToken<T: Object>() -> String {
let lastUpdated = self.findAll(sortedBy: "syncToken").last as? T
if let value = lastUpdated?.value(forKey: "syncToken") { // get value from object by string name
let syncToken = value as! String
return syncToken
} else {
return "00000000000000000000000000000000"
}
}
...
But can't seem to overcome the error of:
Generic parameter 'T' is not used in function signature
I would think the compiler has everything it needs to determine type usage.
Below works for me, I don't know how findAll is defined but the problem is the reference to self as I see it so you need to define T there using associatedtype.
protocol DataProvider: DatabaseLayer {
associatedtype T: Object
func findAll(sortedBy: String) -> T?
}

How to create a dictionary having string and function as key value pair in swift

I am looking to create a dictionary that will have
let urlDict:[String:Func] = ["LOGIN":getLoginURL(), "RESET":getResetPasswordURL()]
func getLoginURL() -> String{
if (sandbox == true){
return sb_login_url
}else{
return live_login_url
}
}
func getResetPasswordURL() -> String{
if (sandbox == true){
return sb_reset_url
}else{
return live_reset_url
}
}
The purpose of this dict is to get/map functions based on the KEY and as per KEY corresponding function must be called which in turn will return return urls.
I have tried naming the dictionary but I am unable to do it
let urlDict:[String:Func] = ["LOGIN":getLoginURL(), "RESET":getResetPasswordURL()]
let urlDict:[String:Function] = ["LOGIN":getLoginURL(), "RESET":getResetPasswordURL()]
let urlDict:[String:Functions] = ["LOGIN":getLoginURL(), "RESET":getResetPasswordURL()]
EDIT 1
class Constants{
private let sb_login_url = "http://IP_ADDRESS_COM/login_with_credentials"
private let live_login_url = "http://google.com"
private let sb_reset_url = "http://IP_ADDRESS_COM/forgot_password"
private let live_reset_url = "http://google.com"
func getLoginURL() -> String{
if (sandbox == true){
return sb_login_url
}else{
return live_login_url
}
}
func getResetPasswordURL() -> String{
if (sandbox == true){
return sb_reset_url
}else{
return live_reset_url
}
}
` let urlDict: [String: () -> String] = ["LOGIN": Constants.getLog‌​inURL(), "RESET":Constants.getResetPasswordURL()]
if let getFunc = urlDict[url_key] {
let url = (getFunc()) // foo}
}
You must specify the type of the functions (which are common for both getLoginURL and getResetPasswordURL) for the value slot in the dictionary, namely () -> String, a zero-arguments function returning a String instance.
func getLoginURL() -> String {
return "foo"
}
func getResetPasswordURL() -> String {
return "bar"
}
let urlDict: [String: () -> String] =
["LOGIN": getLoginURL, "RESET":getResetPasswordURL]
/* ^^^^^^^^^^^- note that you do not _call_ the functions,
as this would result in a String instance */
// get a function reference from the dictionary and invoke it
if let getFunc = urlDict["LOGIN"] {
print(getFunc()) // foo
}
After your comments below, as well as your edit, it seems you want the get... functions to be class members if your class Constants (i.e., marked static).
class Constants {
static var sandbox = true
// I've just added this to test your example
private static let sb_login_url = "http://IP_ADDRESS_COM/login_with_credentials"
private static let live_login_url = "http://google.com"
private static let sb_reset_url = "http://IP_ADDRESS_COM/forgot_password"
private static let live_reset_url = "http://google.com"
static func getLoginURL() -> String {
if (Constants.sandbox == true){
return Constants.sb_login_url
}
else {
return Constants.live_login_url
}
}
static func getResetPasswordURL() -> String{
if (Constants.sandbox == true){
return Constants.sb_reset_url
}
else {
return Constants.live_reset_url
}
}
}
let urlDict: [String: () -> String] =
["LOGIN": Constants.getLoginURL, "RESET": Constants.getResetPasswordURL]
// get a function reference from the dictionary and invoke it
if let getFunc = urlDict["LOGIN"] {
print(getFunc()) // http://IP_ADDRESS_COM/forgot_password
Constants.sandbox = false
print(getFunc()) // http://google.com
}
You could simply create a global typealias to Function prototype and can use it anywhere in your project and you don't need to use () -> String everytime when you create Dictionary.
public typealias VoidToStringFunctio‌n = () -> String
func getLoginURL() -> String {
return "url"
}
func getResetPasswordURL() -> String {
return "password"
}
and use it like this
let urlDict : [String: VoidToStringFunctio‌n] = ["LOGIN": getLoginURL, "Password": getResetPasswordURL]

How do I find the coordinates of a address?

I am making a app that places a point on the coordinates of an address using MapKit.
I have my class made and all my functions set up within the class.
I need to be able to return "getLocation()" which will be the string of an address and use that string with the CLGeoCoder().geocodeAdressString and get the coordinates.
Here are my classes
import UIKit
class Vendor {
private var name = String()
private var type = String()
private var location = String()
private var logo = UIImage()
required init(name: String, type: String) {
self.name = name
self.type = type
self.location = ""
self.logo = UIImage()
}
func getName() -> String {
return self.name
}
func setName(name: String) {
self.name = name
}
func getType() -> String {
return self.type
}
func setType(type: String) {
self.type = type
}
func getLocation() -> String {
return self.location
}
func setLocation(address: String) {
self.location = address
}
func getLogo() -> UIImage {
return self.logo
}
func setLogo(image: UIImage) {
self.logo = image
}
}
Assuming that the location in your vendor class are the street, city and country of the vendor your code might look something like this:
func geoCodeVendor() {
let vendorLocation = myVendor.getLocation
let geocoder = CLGeocoder()
geocoder.geocodeAddressString(vendorLocation) { (placemarks, error) in
self.centerCoordinate = (placemarks?.first?.location?.coordinate)!
//Whather more you want to do in this completion handler.
}
}
Here you have created an instance of Vendor, myVendor in the class where you are geocoding your vendor. In that class you have created a property centerCoordinate = CLLocationCoordinate2D().
Usually, you have a vendor class with the name, address and city in separate properties. For instance:
class Vendor {
private var name = String()
private var type = String()
private var address = String()
private var city = String()
private var logo = UIImage()
}
Then your append the address and city in your getLocation function. Please, remind that geocoding works more accurate when the address is as complete as possible, That is, address (with number, city, country.
Hope this helps.