Swift accessing class variable inside closures - variables

Why in Objective-C I can set instance variable inside a block:
#interface CMVServices : UIViewController
#property (nonatomic, strong) NSMutableArray *times;
#implementation CMVServices
#synthesize times=_times;
and set the _times instance variable inside a block:
(some code )
.
.
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
[_times addObjectsFromArray:objects];
}
but I can't in Swift?
class ViewController: UIViewController
var times :AnyObject[]!
query.findObjectsInBackgroundWithBlock { (objects: AnyObject[]!, error: NSError!) -> Void in
self.times = objects
}
The message is:
(AnyObject[]?) times = parent failed to evaluate: variable not available

Two things:
Swift syntax for Array declarations has changed from AnyObject[] to [AnyObject]
If you're still in the class scope, simply use times instead of self.times.
This works for me in XCode6-Beta4 in a Playground file:
import UIKit
class CMVServices : UIViewController {
var times : [AnyObject] = []
func testFunc() {
func findObjects(objects : [AnyObject]!, error : NSError!) {
times = objects
var test_str = "still evaluating in Playground here"
}
findObjects(["test", "string"], nil)
}
}
var test = CMVServices()
test.testFunc()
and for full-on test of closures themselves, this also works (again, in XCode6-Beta4):
import UIKit
class CMVServices : UIViewController {
var times: [AnyObject] = []
func testClosure() {
{ (objects : [AnyObject]!, error : NSError!) -> Void in
self.times = objects
NSLog("still evaluating")
}(["new", "test", "string"], nil)
}
}
var test = CMVServices()
test.testClosure()
test.times // ["new", "test", "string"]

Related

Invoke Swift closure in Objective-C

I want to invoke a Swift closure in Objective-C.
There is an error like this even though I declared the function:
No visible #interface for “User” declares the selector “isReady”
Swift:
#objcMember
class User:NSObject {
func isReady(isTrue: Bool) -> Bool {
return true
}
}
Objective-C:
User *user = [[User alloc] init];
[_user isReady]. <- error
Add to the function #objc modifier:
#objcMember
class User:NSObject {
#objc public func isReady(isTrue: Bool) -> Bool {
return true
}
}
And add public modifier to the function to allow access from other modules (swift code builds as module and ObjC code should export it and access via open interfaces).

Swift class: Fatal error: Use of unimplemented initializer 'init()' for class

I created a little convenience class for haptics and I try to reference the class in my Objc code:
self.haptics = [[Haptics new] initWithStyle:UIImpactFeedbackStyleLight];
#objc final class Haptics: NSObject {
private var feedbackGenerator: UIImpactFeedbackGenerator? = nil
init?(style: UIImpactFeedbackStyle) {
guard UserFeedback.soundsAndVibrationsEnabled() else { return nil }
self.feedbackGenerator = UIImpactFeedbackGenerator(style: style)
}
func prepare() {
feedbackGenerator?.prepare()
}
func hit() {
feedbackGenerator?.impactOccurred()
}
}
I get a crash on the Haptics class declaration:
Fatal error: Use of unimplemented initializer 'init()' for class 'MyProject.Haptics'
Not quite sure what I'm doing wrong here
[Haptics new]
is a short form for
[[Haptics alloc] init]
therefor init() without parameter is called, but it doesn't exist.
do
self.haptics = [[Haptics alloc] initWithStyle:UIImpactFeedbackStyleLight];

How set title of a WKInterfaceButton?

I'm trying to change the text of a button once it is pressed however it doesn't work. Am i missing something?
i've been trying to figure my problem for hours.
any help would be appreciated.
h. file
#import <WatchKit/WatchKit.h>
#import <Foundation/Foundation.h>
#interface InterfaceController : WKInterfaceController
{
IBOutlet WKInterfaceButton*playpausebtn;
}
-(IBAction)play;
#end
m. file
#import "InterfaceController.h"
#import <WatchConnectivity/WatchConnectivity.h>
#interface InterfaceController() <WCSessionDelegate>
#end
#implementation InterfaceController
- (void)awakeWithContext:(id)context {
[super awakeWithContext:context];
// Configure interface objects here.
}
- (void)willActivate {
[super willActivate];
if ([WCSession isSupported]) {
WCSession *session = [WCSession defaultSession];
session.delegate = self;
[session activateSession];
}
}
- (void)didDeactivate {
// This method is called when watch view controller is no longer visible
[super didDeactivate];
}
-(IBAction)play{
[playpausebtn setTitle:#"sleep"];
}
It is working fine in Swift-3 Xcode-8.1 .
// InterfaceController.swift
// WatchKit Extension
import WatchKit
import Foundation
import WatchConnectivity
class InterfaceController: WKInterfaceController,WCSessionDelegate {
#IBOutlet var textLabel: WKInterfaceLabel!
var session:WCSession?
override func awake(withContext context: Any?) {
super.awake(withContext: context)
// Configure interface objects here.
}
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
checkSupportOfSession()
changeAttributeOfText()
}
#IBOutlet var buttonOutlet: WKInterfaceButton!
override func didDeactivate() {
// This method is called when watch view controller is no longer visible
super.didDeactivate()
}
func checkSupportOfSession() {
if( WCSession.isSupported() ) {
self.session = WCSession.default()
self.session?.delegate = self
self.session?.activate()
}
}
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
print("session")
}
func session(_ session: WCSession, didReceiveMessage message: [String : Any]) {
let message:String = message["textIndex"] as! String
textLabel.setText(message)
print(message)
}
func changeAttributeOfText() {
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.alignment = .left
let font = UIFont.boldSystemFont(ofSize: 12)
let attributes:Dictionary = [NSParagraphStyleAttributeName:paragraphStyle , NSFontAttributeName:font ]
let attributeString:NSAttributedString = NSAttributedString(string: "HELLO", attributes: attributes)
textLabel.setAttributedText(attributeString)
}
//Change the ButtonTitle after click
#IBAction func buttonClicked() {
buttonOutlet.setTitle("textNew")
}}
Demo App

Unwrap UIWindow twice from AppDelegate in Swift

To Get the rootViewController in Objective-c you can use the below line of code
[[[UIApplication sharedApplication] delegate] window].rootViewController
I tried to do the same in swift
UIApplication.sharedApplication().delegate?.window?.rootViewController
But I got this error
'UIWindow?' does not have a member named 'rootViewController'
And the suggestion says that you have to unwrap UIWindow twice UIWindow?? to be
UIApplication.sharedApplication().delegate?.window??.rootViewController
My Question is: Why do I need to unwrap the window twice ?
.
.
I have looked in the API and found that
protocol UIApplicationDelegate : NSObjectProtocol {
optional var window: UIWindow? { get set }
}
The window has one optional
class UIWindow : UIView {
var rootViewController: UIViewController?
}
Also the rootViewController has one optional
.
.
I thought may be because UIWindow in UIApplicationDelegate protocol has optional and UIWindow? so I tried the below in Playground
#objc protocol MyApplicationDelegate {
optional var myWindow: MyWindow? { get set }
}
class MyWindow : NSObject {
var rootViewController: Int?
init(number: Int) {
rootViewController = number
}
}
class MyAppDelegate: MyApplicationDelegate {
var myWindow: MyWindow?
init() {
myWindow = MyWindow(number: 5)
}
}
let myDelegate = MyAppDelegate()
println(myDelegate.myWindow?.rootViewController)
However I can get myWindow with one optional and can successfully log '5'
What am I missing here?
Well; I found the problem with my example
In my example I'm creating object from MyAppDelegate directly which will sure have myWindow property as I'm defining it
Changing the example to be the following
#objc protocol MyApplicationDelegate {
optional var myWindow: MyWindow? { get set }
}
class MyWindow : NSObject {
var rootViewController: Int?
init(number: Int) {
rootViewController = number
}
}
class MyAppDelegate: MyApplicationDelegate {
var myWindow: MyWindow?
init() {
myWindow = MyWindow(number: 5)
}
}
class MyApplication: NSObject {
var myDelegate: MyApplicationDelegate
override init() {
myDelegate = MyAppDelegate()
}
}
let myApplication = MyApplication()
println(myApplication.myDelegate.myWindow??.rootViewController)
I needed to add another class MyApplication that has a property conform toMyApplicationDelegate
So I was able to use myWindow?? first unwrapping for the optional part in the protocol and second one is for the optional declaration in the variable UIWindow?

Making some code only run once

I have some code that I would like to run only once in my MainViewController. It should run every time the user starts the app, but only after the MainViewController has loaded.
I don't want to run it in -(void)applicationDidFinishLaunching:(UIApplication *)application.
Here's the idea I had:
MainViewController.h
#interface IpadMainViewController : UIViewController <UISplitViewControllerDelegate> {
BOOL hasRun;
}
#property (nonatomic, assign) BOOL hasRun;
MainViewController.m
#synthesize hasRun;
-(void)viewDidLoad {
[super viewDidLoad];
if (hasRun == 0) {
// Do some stuff
hasRun = 1;
}
}
Any ideas?
Swift 1,2:
static var token: dispatch_once_t = 0
dispatch_once(&token) {
NSLog("Do it once")
}
Objective-C
static dispatch_once_t once;
dispatch_once(&once, ^ {
NSLog(#"Do it once");
});
Swift 3,4:
dispatch_once is no longer available in Swift. In Swift, you can use
lazily initialized globals or static properties and get the same
thread-safety and called-once guarantees as dispatch_once provided
Apple doc
let myGlobal = { … global contains initialization in a call to a closure … }()
_ = myGlobal // using myGlobal will invoke
// the initialization code only the first time it is used.
I don't see any problem with that code. I like using a BOOL (as you did) and then assigning either YES/NO or TRUE/FALSE just so that the code reads more nicely. I would assign TRUE to firstRun in didFinishLaunching, and set it FALSE after the code executes. In my code these type of conditionals usually look like this:
#synthesize firstRun;
-(void)viewDidLoad {
[super viewDidLoad];
if (firstRun) {
// code to run only once goes here
firstRun = FALSE;
}
}
Possible variation (Swift 5):
// Whether this is the first code run since app (re)started
var firstRun: Bool = true
public final class Foo {
public init() {
if firstRun {
// Code that has to be run only once goes
// HERE
firstRun = false
}
}
}
Since there is no dispatch_once_t ins swift 3 and above, we can use lazy initialized global variables.
Make a lazy global variable
lazy var doOnlyOnce: () -> Void = {
.. add code to run only once here ..
return {}
}()
To run the code:
_ = doOnlyOnce
With Swift2.0, Xcode 7.0
var token: dispatch_once_t = 0
override func viewDidLoad() {
super. viewDidLoad()
dispatch_once(&token) {
println("This is printed only on the first call to test()")
}
println("This is printed for each call to test()")
}
for Swift2.2,Xcode 7.3:
static var token: dispatch_once_t = 0
dispatch_once(&YourClassName.token) {
NSLog("Do it once")
}
Watch out "YourClassName.token"