I just started playing with frameworks in xcode trying to create my own module. When making an iOS app its relatively straightforward where the entry point is ( AppDelegate )
However the framework I've created has nothing like a "main.swift" or similar method.
Do i have to create/specify the entry point on my own?
Thanks
EDIT: The reason i'd like the framework to be runnable is so that i can actually print some output while I'm making it to make sure everything works properly.
(My TL;DR is at the bottom.)
As already stated, there is no entry point like you are thinking. Instead, you should do this:
In your Framework target (I'll assume the framework is named MyFramework):
Add files, classes, properties, subclassed controls, etc. and mark things as public, private, internal, and fileprivate. (See the access level section in the Apple documentation.)
For instance:
public class MyClass1 {
public var property1 = ""
private var property2 = ""
public func myFunc() -> String {
print("Hello World!")
}
}
private class MyClass2 {
var property1 = ""
var property2 = ""
func myFunc() -> String {
return "Hello World!"
}
}
In your app target (again, assuming your framework is named myFramework):
include MyFramework
class ViewController: UIViewController {
func tryThis() {
let myClass1 = myClass1()
print(myClass1.myFunc()) // prints "Hello World!"
// the line below will generate a build error
// as myClass2 is marks private
let myClass2 = myClass2()
}
}
TL;DR
Learn your Access Levels, add code into your Framework target, and import the framework into your app.
A framework doesn't have a traditional entry point like this - it won't ever be run by itself, so it doesn't need one.
To use your framework you would create an app which linked with your framework - the entry point for the app would then call methods from inside your framework.
An answer I got from #deanWombourne points out that if anyone wants to only use the framework project for development as is and develop the framework without integrating it in an app for execution, they can just use the tests provided by the framework for an entry point.
For someone that might be new, all you need to is include unit tests to your project, press on the play button which you normally press on for running and select the wrench icon that writes "test" next to it to run the tests.
Related
OK... this is difficult to explain (and to come up with a title for) but I'll try my best.
We first discovered this when using Carthage to import stuff but setting up a sample project in Xcode (without using Carthage) it seems to have done the same thing.
First, here's a screenshot of the sample project we set up...
The have a target Test20000 and it has a dependency A.
The A framework then has a dependency on B.
Important
The Test20000 app does NOT add B as a direct dependency.
The frameworks
In B there is a struct like...
import Foundation
public struct BType {
public let value = "Hello, B!"
}
In A there is a file like...
import Foundation
import B
public struct AType {
public let value = "Hello, A!"
public func doAThing() {
print(BType().value)
}
}
The App
Now in the Test20000 app we do something like...
import Foundation
import A
struct TestType {
func doSomething() {
let aType = AType()
print(aType.value)
aType.doAThing()
}
}
This works as expected. It prints...
Hello, A!
Hello, B!
If I change the function to something like this...
import Foundation
import A
struct TestType {
func doSomething() {
let bType = BType()
print(bType.value)
}
}
Then this doesn't compile as B is not imported and so BType can't be accessed.
The catch!
However! If you declare an extension in B something like...
extension String {
func doAThingInB() {
print(self)
}
}
Then now... without any changes to the imports and the dependencies, I can now change my app code to...
import Foundation
import A
struct TestType {
func doSomething() {
"Hello, bug!".doAThingInB()
}
}
And this will print out as though the extension is public to the actual App. It sort of "bunny hops" from B, over A and into the app.
I feel like this shouldn't happen at all.
We can't find a way to turn this off or to stop this happening.
Is this a bug?
Is there something we need to do to stop this?
Thanks
I have tried to make use of private module maps to hide the inner framework from being visible to the users of A, but had no luck with it. Could be related to [SR-2896] Private modulemaps do no work properly.
I guess this is the expected behaviour right now. There are multiple proposals on swift.org forums to implement something like you want, for example Namespaces x submodules or a more relevant one #_exported and fixing import visibility.
Relevant parts from the last one:
Today’s Swift is designed more like Java or C# or Python, in that if you import Bar in the implementation of Foo it doesn’t affect clients who import Foo. Or, well, it doesn’t make the top-level names of Bar visible to clients who import Foo.
You still need Bar around, because the compiler doesn’t track whether you’ve used one of its types in Foo’s public interface. (That’s the previous section.)
Extensions in Bar are still made visible to clients who import Foo, because the compiler doesn’t distinguish where extensions come from today.
Operator declarations in Bar are still made visible to clients who import Foo, because the compiler finds operators in a different way than it finds everything else at the top level.
And from one of the last messages:
the general outcome of this discussion is that it's probably not worth doing anything clever for Swift vNext: just add "implementation-only import" and maybe "exported import" and leave the rest alone for now.
I'd be happy to know if there is a workaround for this, but it seems like there is none.
OK... so response from the Swift team...
https://bugs.swift.org/browse/SR-9913
This is something they have known about for a while. And they are not looking to fix it any time soon.
So, yes, it's a bug.
But, no, there isn't any way to fix it.
I guess fixing it would cause more issues than it would fix in terms of breaking changes.
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.)
I have some swift extensions I want to across projects.
I'd like to avoid category pollution though, unless those extensions are requested.
Is it possible to write them so that they only apply if I've done a certain import, like:
import MySwiftExtensions
// Use custom extensions
let x = [1,3,5,7].average()
let y = [1,3,5,7].firstWhere { $0 > 3 }
let z = "campervan".make1337()
I could write these as static methods wrapped in a single letter class (eg: ø.average([1,3,5,7]), like lodash) to achieve the same thing but sometimes you get much more concise usage from instance methods.
You wrote:
I have some swift extensions I want to across projects...
When I have code that I want to use across projects I create a separate framework to hold that code. Then, when I want to use that code in a new project, I embed the framework in that project. Or, for development purposes, I create a workspace that includes the project and the framework. That allows me to work on both at the same time, and then only embed the framework in the final product when it is time to export it.
Once the framework is either embedded or in the same workspace, then you should be able to import it into any individual file in your project with:
import MySwiftExtensions
Any file that does not have the import statement will not have access to the extensions.
EDIT:
Here is a link to a blog that describes how to create a Cocoa Touch Framework. And here is another link that describes in detail how to use workspaces to use frameworks in development projects.
I would like to focus attention on what you reported: "..only apply if I've done a certain import.."
It would also mean you want these extensions can be applyed only to a specific class
As reported in this interesting Apple blog chapter and in the official Apple doc you can handle the "Access Control" of your extension
You can extend a class, structure, or enumeration in any access
context in which the class, structure, or enumeration is available.
Any type members added in an extension have the same default access
level as type members declared in the original type being extended. If
you extend a public or internal type, any new type members you add
will have a default access level of internal. If you extend a private
type, any new type members you add will have a default access level of
private.
Alternatively, you can mark an extension with an explicit access level
modifier (for example, private extension) to set a new default access
level for all members defined within the extension. This new default
can still be overridden within the extension for individual type
members.
/* no access level modifier: default access level will be 'internal' */
extension UIViewSubClass
{
// default access level used: internal
var helloWorld : String {
get {
return "helloWorld"
}
}
}
// modify default access level to public
public extension UIViewSubClass
{
// default access level used: public
var helloWorld : String {
get {
return "helloWorld"
}
}
}
The members of extensions marked private are available within the file where they’re defined, and are not available outside that file. Outside the file where the private extension members were defined, any attempt to use them results in an error, and auto-complete wouldn’t even list them
// modify default access level to private
private extension UIViewSubClass
{
var helloWorld : String {
get {
return "helloWorld"
}
}
}
I don't believe you can do what you want per se, but I've used the following approach to provide functionality to only the specific class that implements an interface:
protocol ExampleProtocol {
}
extension ExampleProtocol where Self: UIViewController{
// extend what you need to here
}
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.
Is it possible to swap or patch an assembly when running a UI test on iOS? Currently I have a project where I have setup Typhoon framework and I am able to patch an assembly like this
var controller: HomeViewController!
override func setUp() {
super.setUp()
// setup assemblies
var blAssembly = BusinessLogicAssembly()
var ctrlAssembly = AppAssembly()
// setup patcher
let patcher = TyphoonPatcher()
patcher.patchDefinitionWithSelector("testManager", withObject: {return FakeManager()})
patcher.patchDefinition(blAssembly.testManager() as TyphoonDefinition, withObject: {
return FakeManager()})
let factory = TyphoonBlockComponentFactory(assemblies: [blAssembly, ctrlAssembly])
factory.attachPostProcessor(patcher)
// get controller
controller = factory.componentForKey("homeViewController") as HomeViewController
// force view to laod
let vcView = controller.view
}
And this is working fine. It patches the TestManager with a stub. But in this case I am manually invoking my view controller. When I run a UI test (where the controllers are handled behind the scenes when the app is launched) is there a way of patching an assembly and providing a mock/stub?
For instance lets say I have a view controller which is calling a web service. The web service logic is wrapped in a separate class and is injected into the view controller with a TyphoonAssembly. Now for my UI tests I do not want to contact my actual web services but just proved sample data. I am imagining doing this by creating a stub of my web service class and returning test data. Can this be achieved with the Typhoon framework because I was unable to do it or find an example anywhere.
I am using Swift but Objective-C answers will work too (as long as it is compatible)
Now there's a few ways with Typhoon that one component can be swapped for another. We could use TyphoonPatcher or modularize assemblies. Or we could even make a custom TyphoonDefinitionPostProcessor or TyphoonInstancePostProcessor.
The problem/solution:
But if I'm understanding your question correctly, the problem is that application-style (default) kind of tests are being used, where:
The TEST_HOST flag is set. Your application classes are visible to your tests.
Integration tests instantiate their own assembly, rather than use the application assembly. While this is normally recommended, its important to be aware that this is a separate, additional assembly to the one used in the main app.
If you want to modify the main app's assembly from tests, we can use defaultFactory. If you're using plist integration:
Create a definition for you app delegate:
dynamic func appDelegate() -> AnyObject {
return TyphoonDefinition.withClass(AppDelegate.self) {
(definition) in
//This is a public instance var of type TyphoonComponentFactory
definition.injectProperty("assembly", with: self)
}
}
. . and then in your AppDelegate:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
factory.makeDefault()
}
. . . now in your tests or wherever you need, you patch the app's assembly, by getting a hook to it:
TyphoonComponentFactory *factory = [TyphoonComponentFactory defaultFactory];
[factory unload]; //clear out any singletons
//Now patch it
Note that you may need to call factory.unload() to clear any instances of TyphoonScopeSingleton before-hand.
Also note that TyphoonPatcher also allows unpatching or winding back if you wish.
Alternative:
Alternatively, and simpler (we recommend simple wherever possible), perhaps you could just start up the app with a different network assembly specifying a different set of assemblies in the application plist.
*NB: In Objective-C any TyphoonComponentFactory can be cast to an Assembly and vice-versa. This is not allowed in Swift, so might create some inconvenient limitations. To be addressed by #253