Inout on Swift class method - swift3

In this excerpt from Big Nerd Ranch Chapter 24, why does takeOwnership() not need to use an inout parameter when it is making a change to asset?
Since it is modifying the asset's owner, I would have expected asset to be in-out.
Thank you in advance!
class Person {
var assets = [Asset]()
init(name: String) {
self.name = name
}
func takeOwnership(of asset: Asset) {
asset.owner = self
assets.append(asset)
}
}

Asset is a class, so it's passed in by reference. Changes made to the asset argument are changes to the object being passed in. If Asset were a struct, it would indeed have to be an inout argument.
Checkout this doc

Related

Pass a published property as binding

I have an ObservableObject with a published dictionary of strings to arrays with arrays with Ints:
class MyObservableObject: ObservableObject {
#Published var myDict: [String: [[Int]]]
}
And I want to pass one array of Ints as a Binding from inside the same class to a function of a different struct:
{
...
func someFunc(key: String, index: Int) {
someStruct.func(myDict[key]![index])
}
...
}
I understand that #Published vars can't be passed as Bindings. I'm still hoping that there's any way to achieve this. I also tried storing a reference to the array in the other struct using an inout variable, also without success.
#Published vars can't be passed as Bindings
It is not true - it is possible, via projected value using '$', but you want to pass not a property, but part of value of a property, and this is different thing.
The context is not clear and this someFunc smells not-well :) - I'd say it is needed some refactoring here, but, anyway, technically it is possible to do what you want using dynamically in-place generated binding, like
func someFunc(key: String, index: Int) {
guard myDict[key] != nil else { return }
someStruct.func(Binding<[Int]>(
get: { self.myDict[key]![index] },
set: { self.myDict[key]![index] = $0 }
))
}

SwiftUI "Cannot use instance member 'numberOfDevice' within property initializer; property initializers run before 'self' is available" error

The bolded line (ie var text: String...) gives a "Cannot use instance member 'numberOfDevice' within property initializer; property initializers run before 'self' is available" error. Do I need an init? If so where? Is there a different solution?
struct PairView: View {
var theClass = BluetoothManager()
init() {theClass.viewDidLoad()}
var body: some View {
List {
ForEach(0..<BluetoothManager.peripheralArray.count) { number in //iterates thru 0 to array's count
ConnectionView(numberOfDevice: number) // create a ConnectionView for each number
}
}
}
}
//-------
struct ConnectionView: View {
var numberOfDevice: Int
**var text: String = (BluetoothManager.peripheralArray[numberOfDevice]?.name)!**
// 'name' is a String property of the B.M. class's array's 'numberOfDevice index.'
var body: some View {
ZStack{
RoundedRectangle(cornerRadius: 10.0).fill(Color.blue)
Text(text).foregroundColor(Color.black)
}
}
}
You can use read-only computed property with short-hand.
var text: String {
return (BluetoothManager.peripheralArray[numberOfDevice]?.name)!
}
The error you encountered means you can't use the numberOfDevice variable to instantiate another variable. However, you can use the number you pass to your init method.
Try the following:
struct ConnectionView: View {
var numberOfDevice: Int
var text: String
init(numberOfDevice: Int) {
self.numberOfDevice = numberOfDevice
self.text = (BluetoothManager.peripheralArray[numberOfDevice]?.name)!
}
...
}
Note: I don't recommend force-unwrapping (!). If possible try to provide a default value.
Also, BluetoothManager looks like a type and not like an instance of a class. Make sure you access the peripheralArray property on the valid object and not on the BluetoothManager type.
You can use lazy keyword for that:
lazy var text: String = (BluetoothManager.peripheralArray[numberOfDevice]?.name)!
What is lazy?
lazy means that it will postpone initialization until someone calls the variable and it will not possible if self is not initialized. So you will be sure self is ready before accessing that value.
Why?
When you call numberOfDevice, you are actually calling self.numberOfDevice, but swift is smart enough to let you not explicitly write self keyword.
The issue here is that self is not initialized yet when you are assigning a value to a variable.
So you need to make sure the variable is initialized BEFORE accessing self.

Global variable in swift 3

I have the variable provincias_ws which has the answer of a service, so far everything is fine but I want to know how I make the variable provincias_ws is global since I want to print it in the textFieldDidBeginEditing method
class ViewCtrl: UIViewController, ValidationDelegate, UITextFieldDelegate {
#IBOutlet weak var txt_ciudadU: SearchTextField!
override func viewDidLoad() {
super.viewDidLoad()
opciones_autocompletado()
txt_ciudadU.delegate = self
}
func opciones_autocompletado(){
txt_provinciaU.inlineMode = true
txt_ciudadU.inlineMode = true
Services.objServices.ServProv().then{
data -> Void in
let res = JSON(data)
let status = res["status"].boolValue
if(status){
let provincias_ws = res["data"]["provincias"] //How to make this variable global
}else{
let error = res["error"]["error_data"].stringValue
print(error)
}
SVProgressHUD.dismiss()
UIApplication.shared.endIgnoringInteractionEvents()
}.catch{
error -> Void in
SVProgressHUD.dismiss()
UIApplication.shared.endIgnoringInteractionEvents()
}
}
func textFieldDidBeginEditing(_ textField: UITextField) {
print(provincias_ws)
}
}
Thank you in advance
One can create a "global" variable (meaning it can be access from anywhere in the project) by defining it outside of the class block.
However, it looks like what you want is a class instance variable.
Your text field txt_ciudadU is already an example of an instance variable or property of the class ViewCtrl.
This would be the same, just without #IBOutlet weak.
So would probably be (assuming it's a String):
var provincias_ws: String?
Note the ? declares it as an optional value because it will not be set until it gets set inside your block. You could also initialize it to some default or empty value (e.g. var provincias_ws = "") or you could use ! to indicate you won't be trying to unwrap it when it's nil, although it's better not to rely on that because it is error-prone. What you choose to do depends on your needs.
And you would put that at the same hierarchical level as your text field variable (i.e. right above or below it, although it could go anywhere really, as long as it's not inside a function or another block).
And you would get rid of the current let next to provincias_ws and prepend it with self. i.e.:
self.provincias_ws = res["data"]["provincias"]
The self. is necessary because it's inside a block and the compiler needs that for context.

Best way to assign the properties of one object to another (Trying to implement Copy function) in swift3

I am trying to deep copy a Swift class instance. As copy and mutableCopy function are available only for NSObject class descendent, I am need to implement the copy function of my own for a Swift class object.
Here is what I did,
protocol Copying {
init(original: Self)
}
extension Copying {
func copy() -> Self {
return Self.init(original: self)
}
}
class A : Copying {
var name : String? = nil
var age : Int? = nil
var address : String? = nil
init(name : String) {
self.name = name
}
required init(original: A) {
//one way
self.name = original.name
self.age = original.age
self.address = original.address
//second way
let originalReflect = Mirror(reflecting: original)
let selfReflect = Mirror(reflecting: self)
for (_, originalAttr) in originalReflect.children.enumerated() {
for (_, attr) in selfReflect.children.enumerated() {
if originalAttr.label == attr.label {
//now I know the value of property and its value as well in original
//but how will I set it to self?
//If A was NSObject I could have said
self.setValue(originalAttr.value, forKey: originalAttr.label!)
}
}
}
}
}
Finally this is how I access the copy
let aInstance = A(name: "Sandeep")
let aCopy = aInstance.copy()
The first method shown in required init(original: A) works absolutely fine, but not scalable. In my project I have base class with 50 odd property and atlas 50 more subclasses. Now I cant write the required init(original: A) for each subclass and add self.property = original.property that looks very much dumb way to solve it.
Second method shows the usage of Mirror to iterate through all the properties of object and but gets stuck as there is no way to set the value of self as self.setValue(for key) is not available for swift classes.
Now I cant change the class type to NSObject for this benefit nor can I convert it to struct.
Reference :
Copy Protocol idea from : deep copy for array of objects in swift
Mirror idea from :
https://www.pluralsight.com/guides/microsoft-net/property-copying-between-two-objects-using-reflection
iterate over object class attributes in Swift
Any way to solve this? Suggestions would be really helpful.

How to mock NSDate in Swift?

I have to test some date calculation but to do so I need to mock NSDate() in Swift. Whole app is written in Swift and I'd like to write test in it as well.
I've tried method swizzling but it doesn't work (or I'm doing something wrong which is more likely).
extension NSDate {
func dateStub() -> NSDate {
println("swizzzzzle")
return NSDate(timeIntervalSince1970: 1429886412) // 24/04/2015 14:40:12
}
}
test:
func testCase() {
let original = class_getInstanceMethod(NSDate.self.dynamicType, "init")
let swizzled = class_getInstanceMethod(NSDate.self.dynamicType, "dateStub")
method_exchangeImplementations(original, swizzled)
let date = NSDate()
// ...
}
but date is always current date.
Disclaimer -- I'm new to Swift testing so this may be a horribly hacky solution, but I've been struggling with this, too, so hopefully this will help someone out.
I found this explanation to be a huge help.
I had to create a buffer class between NSDate and my code:
class DateHandler {
func currentDate() -> NSDate! {
return NSDate()
}
}
then used the buffer class in any code that used NSDate(), providing the default DateHandler() as an optional argument.
class UsesADate {
func fiveSecsFromNow(dateHandler: DateHandler = DateHandler()) -> NSDate! {
return dateHandler.currentDate().dateByAddingTimeInterval(5)
}
}
Then in the test create a mock that inherits from the original DateHandler(), and "inject" that into the code to be tested:
class programModelTests: XCTestCase {
override func setUp() {
super.setUp()
class MockDateHandler:DateHandler {
var mockedDate:NSDate! = // whatever date you want to mock
override func currentDate() -> NSDate! {
return mockedDate
}
}
}
override func tearDown() {
super.tearDown()
}
func testAddFiveSeconds() {
let mockDateHandler = MockDateHandler()
let newUsesADate = UsesADate()
let resultToTest = usesADate.fiveSecondsFromNow(dateHandler: mockDateHandler)
XCTAssertEqual(resultToTest, etc...)
}
}
If you want to swizzle it you need to swizzle a class that is internally used by NSDate and it is __NSPlaceholderDate. Use this only for testing since it is a private API.
func timeTravel(to date: NSDate, block: () -> Void) {
let customDateBlock: #convention(block) (AnyObject) -> NSDate = { _ in date }
let implementation = imp_implementationWithBlock(unsafeBitCast(customDateBlock, AnyObject.self))
let method = class_getInstanceMethod(NSClassFromString("__NSPlaceholderDate"), #selector(NSObject.init))
let oldImplementation = method_getImplementation(method)
method_setImplementation(method, implementation)
block()
method_setImplementation(method, oldImplementation)
}
And later you can use like this:
let date = NSDate(timeIntervalSince1970: 946684800) // 2000-01-01
timeTravel(to: date) {
print(NSDate()) // 2000-01-01
}
As others suggested I would rather recommend introducing a class Clock or similar that you can pass around and get a date from it and you can easily replace it with an alternative implementation in your tests.
Rather than use swizzling you should really design your system to support testing. If you do a lot of data processing then you should inject the appropriate date into the functions which use it. In this way your test injects the dates into these functions to test them and you have other tests which verify that the correct dates will be injected (when you stub the methods that use the dates) for various other situations.
Specifically for your swizzling problem, IIRC NSDate is a class cluster so the method you're replacing is unlikely to be called as a different class will be 'silently' created and returned.