I'm trying to find the correct syntax everywhere and I'm just not understanding it.
How do I pass a configuration to KinWebBrowserViewController?
Here's the KinWebBrowserViewController code converted to Swift (I can also provide the original Objective-C)
/*
Initialize a basic KINWebBrowserViewController instance for push onto navigation stack
Ideal for use with UINavigationController pushViewController:animated: or initWithRootViewController:
Optionally specify KINWebBrowser options or WKWebConfiguration
*/
class func webBrowser() -> KINWebBrowserViewController? {
}
class func webBrowser(with configuration: WKWebViewConfiguration?) -> KINWebBrowserViewController? {
}
And here's where the browser is instantiated:
enum WebviewViewControllerFactory {
static func make(for url: String) -> WebBrowserViewController {
let webBrowser = WebBrowserViewController()
webBrowser.showsURLInNavigationBar = false;
webBrowser.showsPageTitleInNavigationBar = false;
webBrowser.barTintColor = UIColor.navBackground
webBrowser.loadURLString(url);
return webBrowser;
}
}
All I want to do is add the following configuration:
let config = WKWebViewConfiguration()
config.userContentController.add(self, name: "callbackHandler")
And add this configuration to the browser.
This seems like it should be easy, but I have been fighting with Swift syntax for hours trying to get what I feel should be fairly simple to work. I've tried initializers, I've tried additional subclassing, I've tried even discarding this and using WkWebView.
How do I set a single configuration on KinWebBrowser?
Related
I am still a beginner at Swift programming and I am trying to build a very basic iOS app. The app requires users to take a photo and edit them on a photo editor. I am having issues with using editor.photoEditorDelegate = self.
This is what the developer of the photo editor used in his example and it gave no problem but it's not working for me. It is giving an error of:
Cannot assign value of type 'PhotosViewController?' to type 'PhotoEditorDelegate?'
I have tried to fix it with:
editor.photoEditorDelegate = self as? PhotoEditorDelegate
but it just makes the app crash when the editor is called.
I declared the editor with:
let editor = PhotoEditorViewController(nibName:"PhotoEditorViewController",bundle: Bundle(for: PhotoEditorViewController.self))
The error is pretty self explanatory ! You need to add this delegate to your class name "PhotoEditorDelegate"
This is a sample code based on the information you have provided
class PhotosViewController: PhotoEditorDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let editor = PhotoEditorViewController(nibName:"PhotoEditorViewController",bundle: Bundle(for: PhotoEditorViewController.self))
editor.photoEditorDelegate = self
// Do any additional setup after loading the view.
}
}
You need to make your PhotosViewController your PhotoEditorDelegate:
class PhotosViewController: PhotoEditorDelegate {
...
Originally I tried to use something like this:
extension URL: CustomStringConvertible{
public override var description: String {
let url = self
return url.path.removingPercentEncoding ?? ""
}
}
After fixing compiler warning code became:
extension URL{
public var description: String {
let url = self
return url.path.removingPercentEncoding ?? ""
}
}
but
print(fileURL) still shows old URL description with percentages.
You can't override a method in an extension. What you're trying to do isn't possible in Swift. It's possible in ObjC (on NSURL) by swizzling the methods, but this should never be done in production code. Even if you could get the above working in Swift through some trickery, you should never use that in production code for the same reason. It could easily impact you in very surprising ways (for instance, it could break NSCoding implementations that expect description to work a certain way.
If you want this style of string, create a method for it and call that when you want it. Don't modify description in an existing class.
I have created a project to test the Typhoon framework , I have created two classes ApplicationAssembly and CoreAssembly where I inject some properties and constructors and a default Configuration.plist to load data from it.
ApplicationAssembly
public class ApplicationAssembly: TyphoonAssembly {
public dynamic func config() -> AnyObject {
return TyphoonDefinition.configDefinitionWithName("Config.plist")
}
}
CoreAssembly
public class CoreAssembly: TyphoonAssembly {
public dynamic func apiHandler() -> AnyObject {
return TyphoonDefinition.withClass(ApiHandler.self) {
(definition) in
definition.useInitializer("initWithDebugging:debugProcess:mainURL:") {
(initializer) in
initializer.injectParameterWith(TyphoonConfig("debug_mode"))
initializer.injectParameterWith(TyphoonConfig("debug_path"))
initializer.injectParameterWith(TyphoonConfig("api_url"))
}
definition.scope = TyphoonScope.Singleton
}
}
public dynamic func viewController() -> AnyObject {
return TyphoonDefinition.withClass(ViewController.self) {
(definition) in
definition.injectProperty("apiHandler", with:self.apiHandler())
}
}
}
I set in my Info.plist the TyphoonInitialAssemblies first the ApplicationAssembly and then the CoreAssembly.
Everything works fine without exceptions or anything except that the app never enters in AppDelegate neither in the ViewController class. I don't know maybe I missed something in the doc or anything.
What I'm missing here?
Why in debug not enter in the ViewController class that is the initial view controller in Storyboard?
The problem was that the ApiHandler class does not extend NSObject, which is a requirement. This is because Typhoon is an introspective Dependency Injection container. As Swift has no native introspection it uses the Objective-C run-time.
The App should not however have crashed in such an obfuscated way. I have opened an issue to look at how to fail with a meaningful error, rather than infinitely recurse.
After solving the initial problem, I also noted that the init method for ApiHandler passing in a Swift Bool object. This needs to be an NSNumber.
init(debugging : NSNumber, debugProcess : String, mainURL : String) {
self.debugging = debugging.boolValue
self.debugProcess = debugProcess
self.mainURL = mainURL
}
Given that Typhoon uses the Objective-C runtime, there are a few quirks to using it with Swift - the same kinds of rules outlined for using Swift with KVO apply.
I have the below UINib extension method, I was wondering if I can set a delegate for the unarchived view
public class func decodeView<T:UIView>(nibName name:String,className classType:T.Type,delegate:AnyObject) -> T {
let nib = UINib(nibName: name)
let topLevelObjects = nib.instantiateWithOwner(nil, options: nil)
let view = topLevelObjects[0] as T
view.setTranslatesAutoresizingMaskIntoConstraints(false)
//check if view.delegate exists then view.delegate = delegate
return view
}
If you're asking if Swift supports reflection, TL;DR: you need to subclass from NSObject. Else you get limited info.
In this question, Does Swift support reflection? you get a more detailed discussion about the possibilities you have.
Once you have this part cleared, an example of how to obtain a list of properties can be found in this SO Answer
Although a quick & dirty way could be just to try and access the property (using KVC) and catch the exception if it fails. Swift does NOT support Try/Catch/Finally constructs, but this nice hack allows you to write code like:
SwiftTryCatch.try({
// try something
}, catch: { (error) in
println("\(error.description)")
}, finally: {
// close resources
})
I’m currently writing some Swift code in a project that is predominately Objective-C. In our ObjC code, we have a header that declares typedef GPUImageOutput<GPUImageInput> MyFilter;. We can then declare e.g. a #property that can only be a GPUImageOutput subclass that implements GPUImageInput.
(NOTE: GPUImageOutput and GPUImageInput are not defined by me; they are part of the GPUImage library)
Our Swift code doesn't seem to recognize this, even though the header is #imported in our Bridging Header. I’ve tried to replicate the declaration in Swift, but neither of these are proper syntax:
typealias MyFilter = GPUImageOutput, GPUImageInput
typealias MyFilter = GPUImageOutput : GPUImageInput
You can't declare typealias like that.
The best we can do is something like this:
class MyClass {
private var filter:GPUImageOutput
init<FilterType:GPUImageOutput where FilterType:GPUImageInput>(filter:FilterType) {
self.filter = filter
}
func setFilter<FilterType:GPUImageOutput where FilterType:GPUImageInput>(filter:FilterType) {
self.filter = filter
}
func someMethod() {
let output = self.filter
let input = self.filter as GPUImageInput
output.someOutputMethod()
input.someInputMethod()
}
}
In Swift 4 you can achieve this with the new & sign (Below an example of a parameter confirming to UIViewController and UITableViewDataSource:
func foo(vc: UIViewController & UITableViewDataSource) {
// access UIViewController property
let view = vc.view
// call UITableViewDataSource method
let sections = vc.numberOfSectionsInTableView?(tableView)
}
In Swift, something like the following should accomplish your task, but it's different than its ObjC counterpart:
typealias GPUImageOutput = UIImage
#objc protocol GPUImageInput {
func lotsOfInput()
}
class GPUImageOutputWithInput: GPUImageOutput, GPUImageInput
{
func lotsOfInput() {
println("lotsOfInput")
}
}
// ...
var someGpuImage = GPUImageOutput()
var specificGpuImage = GPUImageOutputWithInput()
for image in [someGpuImage, specificGpuImage] {
if let specificImage = image as? GPUImageInput {
specificImage.lotsOfInput()
} else {
println("the less specific type")
}
}
UPDATE: now that I understand where/why you have these types ...
GPUImage seems to have a swift example that does what you want, as Swift-ly as possible.
See here:
class FilterOperation<FilterClass: GPUImageOutput where FilterClass: GPUImageInput>: FilterOperationInterface {
...
The type constraint syntax can be applied to functions, too, and with a where clause, that's probably as good as you're going to get directly in Swift.
The more I tried to understand how to port this somewhat common objc trope, the more I realized it was the most Swift-way. Once I saw the example in GPUImage itself, I was convinced it was at least your answer. :-)
UPDATE 2: So, besides the specific GPUImage example I linked to above that uses Swift, the more and more I think about this, either using a where clause to guard the setter function, or using a computable property to filter the set functionality seems the only way to go.
I came up with this strategy:
import Foundation
#objc protocol SpecialProtocol {
func special()
}
class MyClass {}
class MyClassPlus: MyClass, SpecialProtocol {
func special() {
println("I'm special")
}
}
class MyContainer {
private var i: MyClass?
var test: MyClass? {
get {
return self.i
}
set (newValue) {
if newValue is SpecialProtocol {
self.i = newValue
}
}
}
}
var container = MyContainer()
println("should be nil: \(container.test)")
container.test = MyClass()
println("should still be nil: \(container.test)")
container.test = MyClassPlus()
println("should be set: \(container.test)")
(container.test as? MyClassPlus)?.special()
Outputs:
should be nil: nil
should still be nil: nil
should be set: Optional(main.MyClassPlus)
I'm special
(Optionally, you could also use precondition(newValue is SpecialProtocol, "newValue did not conform to SpecialProtocol") in place of the is check, but that will act like an assert() can crash the app if the case isn't met. Depends on your needs.)
#rintaro's answer is a good one, and is a good example of using a where clause as a guard (both nice functional-ly, and Swift-ly). However, I just hate to write a setFoo() function when computable properties exist. Then again, even using a computable property has code smell, since we can't seem to be able to apply a generic type-constraint to the set'er, and have to do the protocol conformance test in-line.
You can use typealias keyword. Here is how to do it:
typealias MyNewType = MyExistingType
It doesn't matter whether MyExistingType is protocol or function or enum. All it needs to be some type. And the best part is you can apply access control on it. You can say
private typealias MyNewType = MyExistingType
That makes MyNewType is only accessible the context that is defined in.