I'm trying to port an objc app which uses Facebook's KVOController, to Swift. I've been encouraged to look at RC3 as an alternate and more Swiftish approach. I've read some blogs and I'm encouraged to give this a try. But much of the docs and blogs seem to concentrate on sockets and timers as examples. So I have two simple questions right now:
1) Given an objc fragment like:
[self.KVOController observe: self.site keyPath: #"programs" options: NSKeyValueObservingOptionInitial block:^(id observer, id object, NSDictionary *change) {
[self.tableView reloadData];
}];
What is the simple way to rewrite this with RC3 APIs? And what is the RC3 equivalent for self.KVOController unobserve: self.site that happens when that view unloads?
2) It's recommended that I use Carthage to grab the RC3 code. Can I intermix a Cartfile safely with a project that is using cocoa pods already for Alamofire and SwiftyJSON?
Disclaimer :)
I'm also new to the whole ReactiveCocoa concept and especially to RAC 3. I'm playing around with the new framework to learn, so my solution may not be the intended usage of the API. I'd be happy to receive feedback from others and learn the right way.
Here's what I could come up with for your first question:
You can define the property you'd want to observe as a MutableProperty. Below is the most basic example with a String property. Assume you have a view model, it has a String property and you want to observe this in your view controller object.
ViewModel.swift
class ViewModel {
var testProp = MutableProperty<String>("")
}
ViewController.swift
class ViewController: UIViewController {
private let viewModel = ViewModel()
override func viewDidLoad() {
super.viewDidLoad()
let testPropObserver = viewModel.testProp.producer
testPropObserver.start(next: {value in
println("new value \(value)")
})
viewModel.testProp.value = "you should see this in your log"
}
}
For the first question I can only guide you in the right direction. Take a look at the change log to 3.0 describing the shift away from RACObserve() to PropertyTypes. It's a little more explicit and it offers type safety. Here is the source, which I think is your best bet for figuring out how to implement it. You could also take a look at their test cases to see if they have built any tests for the PropertyType protocol and mimic how they've got it set up.
And yes, you'll have no trouble mixing Carthage and CocoaPods in one project, as Carthage is designed to be minimally intrusive. As long as you follow the instructions on their website you should be fine.
Related
I'm using swifts stateMachine from gamePlayKit. It works great, but there is one thing I don't understand: The stateMachineseems to be its own isolated island; from what I can tell there is no way to pass in arguments or get callbacks. And this raises questions like, how do I update the UI?
Lets say I enter combat state:
stateMachine.enter(CombatState.self)
There's no way to pass in an argument. Ok, fine. But then CombatState does its thing and when it's done it goes into another state. BUT, before that I need a callback to the view so that I can remove the units from the board lost in combat like so:
self.removeChildren(in: nodesOutOfAction)
But there's no way to send the nodes out of the stateMachine because there's no possible way to add a completion handler. So how do you work around this problem? To me the stateMacine is pointless as it can´t update the UI.
Its possible to access properties of the state like so:
stateMachine.enter(CombatState.self)
self.removeChildren(in: combatState.nodesToRemove)
But that can't be safe, right? Still, as this is the only option that I know of, this is how I solve it.
I totally recommend you check the DemoBots project from Apple.
You should pass the entity reference on the State Initializer
For example
class CombatState: GKState {
unowned var gameScene: GameScene
required init(gameScene: GameScene) {
self.gameScene = gameScene
}
override func didEnter(from previousState: GKState?) {
gameScene.removeChildren(in: ...)
}
}
With this logic you can decouple a lot of logic inside to the states.
As of iOS 13, Apple recommends storing user state using an NSUserActivity object attached to a scene, so I've been trying to a) better understand how NSUserActivity works and b) implement that in my own code. In working through Apple's documentation, I came across this piece of code:
class var activityType: String {
let activityType = ""
// Load our activity type from our Info.plist.
if let activityTypes = Bundle.main.infoDictionary?["NSUserActivityTypes"] {
if let activityArray = activityTypes as? [String] {
return activityArray[0]
}
}
return activityType
}
I understand what this is doing (it looks at the Info.plist file for an entry called "NSUserActivityTypes", if it exists it tries to get the associated array of activityTypes, and then it reads the first item in the array), but what I don't understand is why. In particular, I don't understand why we're only reading the first item in activityArray. In this case we know the first (and only item) is "com.apple.apple-samplecode.StateRestoration.activity" because we have to manually create that plist entry. But I don't understand why we are hard coding looking at the first item of the array in order to get the activity type, because if we know we're just going to get back the String "com.apple.apple-samplecode.StateRestoration.activity", why not just write the code to be this:
class var activityType: String {
return "com.apple.apple-samplecode.StateRestoration.activity"
}
I've never worked with NSUserActivity before, and I understand that it can be (usually is?) used for things other than state preservation/restoration, so there could be many different kinds of useractivities that your app could support (handoff, Siri integration, etc.). So I would assume that we want our code to be as robust as possible in not making any assumptions about the kinds of NSUserActivity objects we might receive.
Maybe someone who has more experience with NSUserActivity can help explain the ways in which NSUserActivity might be handed to my app, and why we can hard code in the first element of an array, while in other places we want to check if a passed-in activity is the right kind of activity (even though we know our array of supported activities has only one kind of activity, so presumably there's only one kind of activity we'd receive in the first place?).
Also, this isn't unique to Apple's sample code... this blog post also takes a similar approach when reading the Info.plist file:
extension Bundle {
var activityType: String {
return Bundle.main.infoDictionary?["NSUserActivityTypes"].flatMap { ($0 as? [String])?.first } ?? ""
}
}
I do have the same doubt when reading the Apple's example of restoring user state using NSUserActivity, because in my past project I always have a Constant.swift to handle all the hardcode string (something like id, or key), for example:
Constants.swift
struct Constants {
static let userActivityTypeA = "com.apple.apple-samplecode.StateRestoration.activityA"
static let userActivityTypeB = "com.apple.apple-samplecode.StateRestoration.activityB"
}
I understand what this is doing (it looks at the Info.plist file for an entry called "NSUserActivityTypes", if it exists it tries to get the associated array of activityTypes, and then it reads the first item in the array), but what I don't understand is why.
In my opinion, it is not a must to follow the Apple's example (adding the activityType in the info.plist & read it using Bundle.main.infoDictionary). Apple just provide a way that is cleaner & can be easily identify what that String is, by providing a fixed key "NSUserActivityTypes" in the info.plist.
(Imagine someday your colleague pick up your project and he may have no idea what is that reverse domain string in the Constants.swift or wherever you placed it.)
why we can hard code in the first element of an array, while in other places we want to check if a passed-in activity is the right kind of activity (even though we know our array of supported activities has only one kind of activity, so presumably there's only one kind of activity we'd receive in the first place?).
It really depends on how many activityType your apps support, for example, you may want to add few more types in the future, like type A go to ViewController A, and type B will open an In-App browsers in your apps. If you know your apps only support one kind of activity, Yes you don't even need to check it, but in general, we always want to confirm what is received and give corresponding respond.
I think that both sample codes are just dummy ways to explain how states work.
There, they use a single restoration ID (com.apple.apple-samplecode.StateRestoration.activity) but your app may have more than that, so as you say, the sample code wouldn't make sense anymore.
Also note that even when using a single restoration ID you have all the NSUserActivity fields (including userInfo) to help you distinguish between states. Of course, it would be dirty to have very different states sharing the same restoration ID.
I create a framework and I want to support early adopters of Combine and SwiftUI for some of my providers without dropping prior iOS version support. I like declarative style and I often use observable classes/structures for variable states that could be used by a developer. How to add Combine's support without dropping iOS10+ support and without coding two different classes?
I planned to add #if canImport(Combine) extension to such classes so iOS13 users could use it in a native way without writing custom stores. So they could just call .sink or .assign for example and they will receive updates.
I don't know what protocol to adopt. I thought that store is a Publisher, but Publisher can't use .send method when something changes and I don't know how to notify Subscriber so.
public class SomeDataProvider<T> {
private var didChangeHandler: ((T?) -> Void)?
public var value: T? {
didSet {
didChangeHandler?(value)
}
}
public func didChange(handler: #escaping ((T?) -> Void)) {
self.didChangeHandler = handler
}
public init() {}
}
The best approach also is to emulate at least .sink (most usable) for prior versions of iOS, so we don't use two separate methods or different iOS versions.
This is something that this repo (OpenCombine) is handling, the idea you have is really great but Combine is too complicated to build. The project is in early development, that's because Combine is new to the world.
I'm a seasoned Objective-c programmer but I can't say the same for Swift, I'm having a hard time unit testing a class in swift without using frameworks like OCMock.
The Problem: I'm integrating Firebase into a mixed Objective-C/Swift project, and I need to configure it based on the build configuration of the app.
I've written a Swift class for that (that will be used by the obj-c app delegate), however since the firebase framework is configured trough a static class method, precisely FIRApp.configure(with: FIROptions), I need to mock this method somehow in order to unit test it.
My code, without any handle for Dependency Injection, looks like that:
#objc class FirebaseConfigurator: NSObject{
func configureFirebase(){
let config = configManager.buildConfiguration
var optionsPlistBaseName = getPlistName()
let optionsFile = Bundle.main.path(forResource: optionsPlistBaseName, ofType: "plist")
guard let opts = FIROptions(contentsOfFile: optionsFile) else{
assert(false, "fatal: unable to load \(optionsFile)")
return
}
FIRApp.configure(with: opts)
}
func getPlistName() -> String{
// retrieves correct plist name and returns it
}
}
I've done some research but so far I didn't find nothing that fits my solution, however I was thinking of one of the following:
I could pass a function that defaults to FIRApp.configure(with:) however I should do this from objective-c and the function also accepts a parameter, I was struggling with the syntax
I could use a wrapper around FIRApp, but I wanted to avoid it unless the only viable clean solution.
I could keep on playing with protocols and do dependency inversion, however being the method static I was struggling with the syntax again, I can't find an easy way to do DI with a mock class with a static method.
As a reference (both personal and for who might need it) these are some of the resources I found useful and upon which I will keep on digging:
Dealing with static cling in Swift
This Question
This article about generic unit testing
In the meanwhile, every help would be really appreciated.
As a sidenote, there are many ways I can solve this problem without struggling with mocking a static class method, but my aim here is to find out a way of mocking it in order to have a better understanding of the best practices when testing more complex situations.
You can indeed do any of those.
Closure Argument
You can have your configureFirebase function take an "applier" closure that defaults to what you originally used:
func configureFirebase(
using apply: (_ options: FIROptions) -> Void
= { opts in FIRApp.configure(opts) }
) {
// building |opts| as before
// Now replace this: FIRApp.configure(with: opts)
apply(opts)
}
Protocols
You need a Configurable protocol, and then to conform FIRApp to it for the default case:
protocol Configurable {
static func configure(with options: FIROptions)
}
extension FIRApp: Configurable {}
class FirebaseConfigurator {
var configurable: Configurable
init(configurable: Configurable = FIRApp) {
self.configurable = configurable
}
func configureFirebase() {
//load |opts|…
configurable.configure(with: opts)
}
}
If you're just going to use this in one method, though, it's merely transient state, and it should probably be a function argument rather than stored property.
(If it's unclear whether it's persistent or transient state because the whole point of the class is to call a single function, perhaps you don't even need a class, just a function.)
We have a lot of blogs, where Swift and it's awesomeness is discussed. Most of them sound like a whole WWDC and don't discuss their topics in an exhausting way and a critical point of view. So this question is explicit for the free thinking minds, the stackoverflow community ;-)
In Swift we have access control mechanisms like
public private(set) var foo: Foo?
and
private var _foo: Foo?
public var foo: Foo? {
get {
// make some preparing stuff, in another kind of way, as in the setter
return _foo
// make some completing stuff, in another kind of way, as in the setter
}
willSet {
// make some preparing stuff
}
set {
_foo = newValue
}
didSet {
// make some completing stuff
}
The common way in OOP for this purpose is
private var foo: Foo?
public getFoo() -> Foo? {
// make some preparing stuff, in the same kind of way, as in the setter
return foo
// make some completing stuff, in the same kind of way, as in the setter
}
private setFoo(foo: Foo) {
// make some preparing stuff
self.foo = foo
// make some completing stuff
}
What do you think about these mechanisms, considering following questions?
Why willSetand didSet but no willGetand didGet?
Is there no better way than these double defining variables fooand _foo?
public private(set) gives good access control, but the IDE Xcode shows suggestions to the variable, when typing myInstance.foo = from outside the declaring class. No Error or warning is shown until you assign a new value.
After assigning the new value, Xcode says the setter is inaccessible. In OOP usually visibility is checked at first. Swift seems not to know what visibility is. Obviously it's not possible to determine if myInstance.foo will be completed to an "read command" myInstance.foo.bar or a "write command". What do you think about this lack of useful code completion?
Has anyone doe's performance checks aiming at the use of explicit getters and setters?
Is it a good idea to use computed variables. They are in fact nothing else than the results of methods/functions.
Apple and it's community promote the meaningfulness or awesomeness of these techniques intensively. a) Is it bad to use traditional Getters and Setters? b) Why does Apple use "their" approach in common iOS UserInterface Classes (textField.text = "foo"), in contrast to their youngest classes in the UserInterface of the WatchKit (textField.setText("foo"))? Is it a step back of forward?
So. These are many questions, but I thinks, at least these are relevant, to get an overview. I'm looking forward for your aspects.