How do I Unit Test a private struct, not private methods of a struct? - swift

Here is some example code. How can I get access to it in an XCTest?
private struct Thing {
let name:String
let age:Int
init(name:String,age:Int) {
self.name = name
self.age = age
}
}
I've tried such things as
#if DEBUG
private struct Thing {
#else
struct Thing {
#end
let name:String
let age:Int
init(name:String,age:Int) {
self.name = name
self.age = age
}
}
which, of course, doesn't compile.

Related

How to initialize stored property which is of type associatedtype protocol

protocol Identifiable {
associatedtype ID
func identifier() -> ID
}
protocol PersonProtocol: Identifiable {
var name: String { get }
var age: Int { get }
}
class Person: PersonProtocol {
let name: String
let age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
func identifier() -> String {
return "\(name)_\(age)"
}
}
I tried to a declare & initialise stored property as let owner: PersonProtocol in class Car but it gave an error:
`PersonProtocol' can only be used as a generic constraint because it has Self or associated type requirements
Hence I tried following piece of code to do the same, but I am not sure if this is the correct way to do this.
Need suggestions.
class Car<T: PersonProtocol>{
let owner: T
init<U: PersonProtocol>(owner: U) where U.ID == String {
self.owner = owner as! T // I am force casting `U` as `T`. is this forcecasting justified ?
}
func getID() -> String {
owner.identifier() as! String // is this forcecasting justified ?
}
}
class Car<U,T: PersonProtocol> where T.ID == U{
let owner: T
init(owner: T) {
self.owner = owner
}
func getID() -> U {
owner.identifier()
}
}
let person = Person(name: "John Snow", age: 34)
let car = Car<String, Person>(owner: person)

Can't cast a value from a subclass Swift

I made a lot of research but I didn't find an answer to my question. Others talk about basic issues with Swift classes. Still I have an issue with my own classes. I also read courses about classes but it didn't help me.
I have two classes; one of them inherit from the other.
Here is my classes code :
class GlobalUser {
var uid: String!
var publicName: String!
var pushID: String!
var firstName: String!
var lastName: String!
var example1: [String:String]!
var fullName: String! {
get {
return firstName + " " + lastName
}
}
init(document: DocumentSnapshot) {
guard let data = document.data() else {
print("Missing user information during initialization.")
return
}
self.uid = document.documentID
self.publicName = (data["publicName"] as? String)!
self.pushID = (data["pushID"] as? String)!
self.example1 = (data["example1"] as? [String : String])!
let name = data["name"] as? [String:String]
self.firstName = (name!["firstName"])!
self.lastName = (name!["lastName"])!
}
}
class InterestingUser: GlobalUser {
var code: Int?
var example: [String:String]?
var number: Int! {
get {
return example.count
}
}
override init(document: DocumentSnapshot) {
super.init(document: document)
}
}
And then I try to cast a GlobalUser to a InterestingUser like this :
if let interestingUser = user as? InterestingUser {
...
}
But this cast always fails...
Any idea? Thanks in advance for your help.
The error you're experiencing is due to this statement from your question: 'And then I try to cast a GlobalUser to a InterestingUser like this...' and is due to inheritance.
Your GlobalUser class is the superclass. Your InterestingUser is a subclass of your GlobalUser.
So your InterestingUser class 'knows' about the GlobalUser because it is it's parent and you can cast InterestingUser as? GlobalUser but not the other way around.
Example:
if let interstingUser = InterestingUser() as? GlobalUser {
// this will succeed because InterestingUser inherits from GlobalUser
}
if let globalUser = GlobalUser() as? InterestingUser {
// this will fail because GlobalUser is not a subclass of InterestingUser
}
Here's some playground code for you to test with:
class GlobalUser {
}
class InterestingUser: GlobalUser {
}
class Demo {
func comparison() {
let interesting = InterestingUser()
let global = GlobalUser()
if let intere = interesting as? GlobalUser {
print("Interesting is global as well")
}
if let global = global as? InterestingUser {
print("Global is interesting")
}
}
}
let demo = Demo()
demo.comparison()
// prints 'Interesting is global as well'

Swift optional chaining with non-nil value

I have the following program which works fine -
class Person {
var pet: Pet?
}
class Pet {
var name: String
var favoriteToy: Toy?
init(name: String) {
self.name = name
}
}
class Toy {
var name: String
init(name: String) {
self.name = name
}
}
let q = Person()
// Pet(name:"goofy")
// Toy(name:"teddy")
if let someToy = q.pet?.favoriteToy?.name {
print("This person's pet likes \(someToy).")
} else {
print("This person's pet does not have a favorite toy")
}
Prints:
This person's pet does not have a favorite toy
How to use the above code modified to print "This person's pet likes teddy."?
I know I will have not be able to use if let as there will be nothing to unwrap. So I have to write something like this instead of if let:
let someToy = q.pet.favoriteToy.name
print("This person's pet likes \(someToy).")
Should print "This person's pet likes the teddy."
I also know I have to put non-nil value something like this:
class Person {
var pet = Pet ()
}
Still I am having initialization problems. How to go about it?
This should solve your immediate needs...
let man = Person()
let goofy = Pet(name: "goofy")
let squeaky = Toy(name: "squeaky toy")
goofy.favoriteToy = squeaky
man.pet = goofy
But if a person is usually going to be initialized with a pet and a toy, and both of those classes are initialized with a string, then you might want to define a convenience initializer:
class Pet {
var name: String
var favoriteToy: Toy?
init(name: String) {
self.name = name
}
}
class Toy {
var name: String
init(name: String) {
self.name = name
}
}
class Person {
var pet: Pet?
convenience init(petName: String, toyName: String) {
self.init()
let toy = Toy(name: toyName)
let pet = Pet(name: petName)
pet.favoriteToy = toy
self.pet = pet
}
}
func test() {
let bill = Person(petName: "goofy", toyName: "squeaky ball")
guard let toyName = bill.pet?.favoriteToy?.name else { return }
print(toyName)
}
test()
One more thing: optional chaining can be combined with a guard statement to great effect in Swift.
Use ? = nil in your initializer to pass in some optional parameters.
class Person {
var pet: Pet?
init(pet: Pet? = nil) {
self.pet = pet
}
}
class Pet {
var name: String
var favoriteToy: Toy?
init(name: String, toy: Toy? = nil) {
self.name = name
self.favoriteToy = toy
}
}
class Toy {
var name: String
init(name: String) {
self.name = name
}
}
let q = Person(pet: Pet(name: "goofy", toy: Toy(name: "teddy")))
// Pet(name:"goofy")
// Toy(name:"teddy")
if let someToy = q.pet?.favoriteToy?.name {
print("This person's pet likes \(someToy).")
} else {
print("This person's pet does not have a favorite toy")
}

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.

Cannot assign to the result of this expression with generics

I have the following generic class where I want to manage a string hash:
class NamedProfile<T> {
private var set = [String:T]()
private var profiles = [String]()
private let userDefaults = NSUserDefaults.standardUserDefaults()
private let profileName:String
var currentSet = ""
init(name:String, set:[String:T]) {
profileName = name
self.set = set
if let existingProfiles = userDefaults.objectForKey(name) as? [String] {
profiles = existingProfiles
}
for key in profiles {
if let existingProfile = userDefaults.objectForKey(profileNamed(name)) as? T {
set[key] = existingProfile // <-- error
}
}
}
private func profileNamed(name:String) -> String { return "\(profileName) \(name)" }
}
Why does the compiler croak in the above assignment?
In
init(name:String, set:[String:T]) {
// ...
set[key] = existingProfile // <-- error
// ...
}
set refers to the (immutable) method parameter.
Use self.set instead to refer to the property.