How can make a function which works just some platform? - swift

I can use #if directly in my codes and it works just fine, however I want make a reusable function and call it in other places as well, I made this function:
func runOniOS(action: () -> Void) {
#if os(iOS)
action()
#endif
}
And I am using it in my multi platform project for macOS like this:
runOniOS(action: {
let redColor: UIColor = .red
})
But Xcode take it as an error and say:
Cannot find type 'UIColor' in scope
How can I make a reusable function?

Related

Invoke code in extension on object deinit?

In Swift (I'm using 4.1), is there a way to do some clean-up in an extension when an object is being destructed? I have in mind the kind of code that would go in deinit(), but an extension can't declare deinit(). (Besides which, if several extensions needed to do this, there would be multiple deinit() declarations.)
I did not find a way to exactly get what you want, but maybe this code will help. I have never tried it, so maybe use it more as an inspiration. In a nutshell, it allows you to add bits of code that will excute on deinitialization.
/// This is a simple object whose job is to execute
/// some closure when it deinitializes
class DeinitializationObserver {
let execute: () -> ()
init(execute: #escaping () -> ()) {
self.execute = execute
}
deinit {
execute()
}
}
/// We're using objc associated objects to have this `DeinitializationObserver`
/// stored inside the protocol extension
private struct AssociatedKeys {
static var DeinitializationObserver = "DeinitializationObserver"
}
/// Protocol for any object that implements this logic
protocol ObservableDeinitialization: AnyObject {
func onDeinit(_ execute: #escaping () -> ())
}
extension ObservableDeinitialization {
/// This stores the `DeinitializationObserver`. It's fileprivate so you
/// cannot interfere with this outside. Also we're using a strong retain
/// which will ensure that the `DeinitializationObserver` is deinitialized
/// at the same time as your object.
fileprivate var deinitializationObserver: DeinitializationObserver {
get {
return objc_getAssociatedObject(self, &AssociatedKeys.DeinitializationObserver) as! DeinitializationObserver
}
set {
objc_setAssociatedObject(
self,
&AssociatedKeys.DeinitializationObserver,
newValue,
objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC
)
}
}
/// This is what you call to add a block that should execute on `deinit`
func onDeinit(_ execute: #escaping () -> ()) {
deinitializationObserver = DeinitializationObserver(execute: execute)
}
}
How it works
Now let's assume you have your object, let's call it A and you want to create some code inside an extension, you can use onDeinit to add your clean-up code like this:
extension A {
func someSetup() {
// Do your thing...
onDeinit {
/// This will be executed when A is deinitialized
print("Execute this")
}
}
}
You can also add it from outside the extension,
let a = A()
a.onDeinit {
print("Deinitialized")
}
Discussion
This is different than what you want in that instead of definition a function (deinit) you need to call one. But anyway in protocol extensions, you can never really use the underlying object's life cycle, so it should be okay for most cases.
I am not sure about the order of execution between A's deinit and the DeinitializationObserver's deinit. You might need to drop the assumption that A is still in memory when you write the code inside the closure.
If you need more than one onDeinit you can update the associated object to retain an array of DeinitializationObserver
In my code, I usually use ReactiveCocoa's Lifetime for this kind of things. However, it is more complicated than what I wrote here. Instead, it looks like they swizzle the dealloc selector. If you're interested you can take a look inside - https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/ReactiveCocoa/NSObject+Lifetime.swift

In Swift, can you write an extension which returns a typed instance of the class the extension is on?

This is one of those things that seems simple enough, but doesn't work as you'd expect.
I'm working on a 'fluent/chaining'-style API for my classes to allow you to set properties via functions which can be chained together so you don't have to go crazy with initializers. Plus, it makes it more convenient when working with functions like map, filter and reduce which share the same kind of API.
Consider this RowManager extension...
extension RowManager
{
#discardableResult
public func isVisible(_ isVisible:Bool) -> RowManager
{
self.isVisible = isVisible
return self
}
}
This works exactly as one would expect. But there's a problem here... if you're working with a subclass of RowManager, this downcasts the object back to RowManager, losing all of the subclass-specific details.
"No worries!" I thought. "I'll just use Self and self to handle the type!" so I changed it to this...
extension RowManager
{
#discardableResult
public func isVisible(_ isVisible:Bool) -> Self // Note the capitalization representing the type, not instance
{
self.isVisible = isVisible
return self // Note the lowercase representing the instance, not type
}
}
...but that for some reason won't even compile giving the following error...
Command failed due to signal: Segmentation fault: 11
UPDATE
Doing more research, this seems to be because our code both is in, and also uses, dynamic libraries. Other questions here on SO also talk about that specific error in those cases. Perhaps this is a bug with the compiler because as others have correctly pointed out, this code works fine in a stand-alone test but as soon as the change is made in our code, the segmentation fault shows up.
Remembering something similar with class functions that return an instance of that type, I recalled how you had to use a private generic function to do the actual cast, so I tried to match that pattern with the following...
extension RowManager
{
#discardableResult
public func isVisible(_ isVisible:Bool) -> Self // Note the capitalization
{
self.isVisible = isVisible
return getTypedSelf()
}
}
private func getTypedSelf<T:RowManager>() -> T
{
guard let typedSelfInstance = self as? T
else
{
fatalError() // Technically this should never be reachable.
}
return typedSelfInstance
}
Unfortunately, that didn't work either.
For reference, here's the class-based code I attempted to base that off of (className is another extension that simply returns the string-representation of the name of the class you called it on)...
extension UITableViewCell
{
/// Attempts to dequeue a UITableViewCell from a table, implicitly using the class name as the reuse identifier
/// Returns a strongly-typed optional
class func dequeue(from tableView:UITableView) -> Self?
{
return self.dequeue(from:tableView, withReuseIdentifier:className)
}
/// Attempts to dequeue a UITableViewCell from a table based on the specified reuse identifier
/// Returns a strongly-typed optional
class func dequeue(from tableView:UITableView, withReuseIdentifier reuseIdentifier:String) -> Self?
{
return self.dequeue_Worker(tableView:tableView, reuseIdentifier:reuseIdentifier)
}
// Private implementation
private class func dequeue_Worker<T:UITableViewCell>(tableView:UITableView, reuseIdentifier:String) -> T?
{
return tableView.dequeueReusableCell(withIdentifier: reuseIdentifier) as? T
}
}
At WWDC Apple confirmed this was a Swift compiler issue that something else In our codebase was triggering, adding there should never be a case where you get a Seg11 fault in the compiler under any circumstances, so this question is actually invalid. Closing it now, but I will report back if they ever address it.

Async communication of swift and Javascriptcore

I want to use the Async functionality offered by WKWebView outside web view.
The JS Context option does not provide the Async functionality.
In WKWebView, I write my logic as follows.
func swiftFunc1() {
webView.evaluateJavaScript("jsFunc1(), completionHandler: nil)
}
In javascript code I post a message to swift
function jsFunc1() {
window.webkit.messageHandlers.myMsg.postMessage("call swiftFunc2");
}
The swift code can then call appropriate JS callback as a part of handling message.
But this is dependent upon the webview being the foreground view. If I want to use the JS logic independent of the webview, JSContext is the option. I tried following
func swiftFunc1() {
myCtxt = JSContext()
exportedToJS = exportToJS() //confirms to JSExport and uses #objc
myCtxt.setObject(exportedToJS.self, forKeyedSubscript: "swiftIface")
myFunc = myCtxt.objectForKeyedSubscript("jsFunc1")
myFunc.callWithArguments(nil)
}
Now in javascript code I cannot post a message to swift. If I try to call a swift function as follows, code gets stuck forever.
function jsFunc1() {
swiftIface.swiftFunc2() // This creates a deadklock
}
How can I achieve either of the following without "returning" from the called Javascript function jsFunc1()?
Either post a message to swift so that it can take appropriate action
Or call a swift function so that the appropriate action is taken
Do I understand you right, if you do not want your javscript to terminate after execution?
If I understood you wrong, maybe following helps (I am not at home at Mac to test, but maybe it works if you modify your code as follows).
Option 1: Blocks
The swiftFunc1 could look like this:
func swiftFunc1() {
myCtxt = JSContext()
myCtxt.setObject(unsafeBitCast(swiftFunc2, AnyObject.self), forKeyedSubscript: "swiftFunc2")
exportedToJS = exportToJS() //confirms to JSExport and uses #objc
myCtxt.evaluateScript("swiftFunc2()")
}
Your swiftFunc2 would look like this:
let swiftFunc2: #convention(block) Void -> Void = {
// do something here
}
Your JS code would look like this:
function jsFunc1() {
swiftFunc2();
}
Option 2: JSExport
Your have an exported class which is accessible for all javascript:
import Foundation
import JavaScriptCore
#objc class JavascriptHandler: NSObject, JavascriptHandlerExport {
let context: JSContext = JSContext()
init () {
context.setObject(self, forKeyedSubscript: "MyJSHandler") // set the object name for self accessible in javascript code
}
func swiftFunc1() {
context.evaluateScript("MyJSHandler.swiftFunc2();")
}
func swiftFunc2 () {
// do something here
}
}
Your protocol for the exported class.Here you have to declare all properties and methods you want to use with Javascript.
import Foundation
import JavaScriptCore
#objc protocol JavascriptHandlerExport: JSExport {
func swiftFunc2 ( ) -> Void
}
With this it should be possible for you to call a function from javascript and still let it continue.
You can now access the functions of the class JavascriptHandler from Javascript like this in this example:
MyJSHandler.swiftFunc2();
If you want to seperate the class where your WebView is from the one where your JS logic lies that should also not be a problem. You should also be able to combine the block syntax with the JSExport method.
Let me know if it's not working/behaving as wanted.

Swift Generics and Protocol Extensions

I have a protocol Reusablethat has a static function static func reuseId() -> String and a protocol extension that defines the default implementation for the function. Then, I implemented a extension on UITableViewCell to conform to the Reusable protocol. I can now use the function on my TableViewCells without a problem: SomeTableViewCell.reuseId().
The Problem I have is with Generics. I have a generic class that has a generic parameter of the type UITableViewCell:
internal class SomeClass<CellType: UITableViewCell>: NSObject {
...
}
I want to be able to use the function specified in Reusable in my generic class on CellType but unfortunately this does not work as expected. The compiler always generates the error Type 'CellType' has no member 'reuseId'.
Does anybody know why this is happening? Is there a workaround?
I am using Xcode 7.0 with Swift 2.0.
Greetings from Germany
UPDATE: Here is some sample code that better shows my problem:
import UIKit
protocol Reusable {
static func reuseId() -> String
}
extension Reusable {
static func reuseId() -> String {
return String(self).componentsSeparatedByString(".").last!
}
}
extension UITableViewCell: Reusable { }
class SomeGenericClass<CellType: UITableViewCell> {
func someFunction() {
let reuseIdentifier = CellType.reuseId()
}
}
This Code will generate the above error but I do not quite understand why this happens. I think the main difference to the sample code that jtbandes posted is that I use a static function.
UPDATE: The issue is fixed in Xcode 8.3 beta 2. The sample code above now works as expected (after migrating it to Swift 3 of course).
That's an interesting problem. Your code seems like it should work; you might want to file an enhancement request.
Here's a workaround that seems to work correctly:
class SomeGenericClass<CellType: Cell> {
func someFunction() {
let reuseIdentifier = (CellType.self as Reusable.Type).reuseId()
}
}
Another (workaround) way to get what you need:
class GenericExample<CellType:UITableViewCell where CellType:Reusable>
{
func test() -> String {
return CellType.reuseId()
}
}
GenericExample<UITableViewCell>().test() // returns "UITableViewCell"
GenericExample<MyCell>().test() // returns "MyCell"

How to determine which object/class is calling a global Swift function?

With this code:
func externalFunc() {
println("How can I know which object/class is calling me?")
}
class Test {
func callExternalFunc() {
externalFunc()
}
}
In the Objective-C runtime objc_msgSend passes two hidden parameters to every message we send. They are self and _cmd. (Source)
In the above example, is there any way to know who is calling externalFunc?
I'm not sure if there is a way to obtain this automatically, but you can get this info if you add a default param of type String to the function and set it to #function.
For example...
func externalFunc(callingFunctionName: String = #function) {
println("Calling Function: \(callingFunctionName)")
}
Then you would call it without the added default param...
let test = Test()
test.callExternalFunc()
And it would print the following...
"Calling Function: callExternalFunc()"
If you are willing to modify the method signature you could do something like below:
func externalFunc(file: String = #file, line: Int = #line) {
print("calling File:\(file) from Line:\(line)")
}
From apple's swift blog
Swift borrows a clever feature from the D language: these identifiers
(__FILE__ & __LINE__ ) expand to the location of the caller when
evaluated in a default argument list.
Note that __FILE__ and __LINE__ have been depreciated in Swift 2.2 and have been removed in Swift 3. They are replaced by #file, and #line.
Here's a great utility class I found on github:
https://github.com/nurun/swiftcallstacktrace
Use like this:
let callingMethodInfo = CallStackAnalyser.getCallingClassAndMethodInScope(false)
if let callingMethodInfo = callingMethodInfo {
NSLog("class: %#", callingMethodInfo.0)
NSLog("method: %#", callingMethodInfo.1)
}
In your question you are mentioning self and _cmd.
self is accessible in Swift exactly in the same way as in Obj-C (which is logical).
_cmd (selector of the current method) is not accessible. There is no reason for it to be accessible, Swift doesn't use selectors outside Obj-C contexts (in pure Swift you cannot call selectors dynamically). The only use case for it is to print the name of the current function for debugging purposes. The same can be achieved in Obj-C (or C) using __FUNCTION__ macro. The same can be achieved in Swift:
func getCurrentFunctionName(functionName: StaticString = #function) -> String {
return String(functionName)
}
func externalFunc() {
print("Function name: \(getCurrentFunctionName())") // prints "externalFunc"
}
Note that in your example externalFunc is a function, not a method. Even in Obj-C neither self or _cmd wouldn't be available for it.
If you want to know who has called your method (and I really suppose you want to know it for debugging purposes), then you can inspect your call stack:
func externalFunc() {
let stackSymbols = NSThread.callStackSymbols()
stackSymbols.forEach {
let components = $0.stringByReplacingOccurrencesOfString(
"\\s+",
withString: " ",
options: .RegularExpressionSearch,
range: nil
).componentsSeparatedByString(" ")
let name = components[3]
let demangledName = _stdlib_demangleName(name)
print("\(demangledName)")
}
}
which prints (for my project called SwiftTest):
SwiftTest.externalFunc () -> ()
SwiftTest.Test.callExternalFunc () -> ()
main
start
0x0