ViewController does not conform to protocol MSBandClientManagerDelegate - microsoft-band

I am trying to use Swift to implement the Microsoft Band SDK. I keep getting this error when trying to set up my code.
class ViewController: UIViewController, UITableViewDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate, MSBClientManagerDelegate, UIScrollViewDelegate {
I have never seen this before, but I have also never tried to convert an Objective C sample to Swift.
Any help would be appreciated!
EDIT: Here is the protocol from Objective C
#protocol MSBClientManagerDelegate<NSObject>
- (void)clientManager:(MSBClientManager *)clientManager clientDidConnect:(MSBClient *)client;
- (void)clientManager:(MSBClientManager *)clientManager clientDidDisconnect:(MSBClient *)client;
- (void)clientManager:(MSBClientManager *)clientManager client:(MSBClient *)client didFailToConnectWithError:(NSError *)error;
#end
EDIT 2: After using suggested Swift Helper class
This is how I am trying to set up the connection.
var clients:NSArray = bandHelper.attachedClients()!
var firstClient: MSBClient = clients[0] as MSBClient
if (clients.count == 0){
println("The band is not detected")
return
}
I have no clue how this should be set up
bandHelper.connectClient(firstClient, {completion: (connected:true -> void in)})
println("Please wait...connecting to band")
Then, when trying to send a photo to the band, this function does not work
bandHelper.client?.personalizationManager.updateMeTileImage(bandScaledImage, { (completionHandler: NSError!) -> Void in
NSLog("%#", NSError())})
I am getting thrown off by using the helper class. Any help would be appreciated!

Sample Project
I linked a sample Swift project for Microsoft Band Kit iOS that can send a haptic to the band. Find the link here: http://droolfactory.blogspot.com/2015/03/ios-swift-example-of-connecting-with.html
Microsoft Band Bridging Header
First to convert the Objective-C classes to be used with Swift, create a Bridging Header. Mine look like this for just the MicrosoftBandKit-iOS framework:
#ifndef ModuleName_Bridging_Header_h
#define ModuleName_Bridging_Header_h
#import <MicrosoftBandKit_iOS/MicrosoftBandKit_iOS.h>
#endif
Make sure to replace the ModuleName with the name of your apps Module. Find more on Bridging Header files at: https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html
Band Helper Class
Next I wrapped the MSBClientManagerDelegate in a helper class (BandManager) which uses a singleton to manage the Band. I have a gist for it here (https://gist.github.com/mthistle/8f6eb30c68a918fc6240)
The code for this the gist is:
import Foundation
let kConnectionChangedNotification = "kConnectionChangedNotification"
let kConnectionFailedNotification = "kConnectionFailedNotification"
private let _SharedBandManagerInstance = BandManager()
class BandManager : NSObject, MSBClientManagerDelegate {
private(set) var client: MSBClient?
private var connectionBlock: ((Bool) -> ())?
private var discoveredClients = [MSBClient]()
private var clientManager = MSBClientManager.sharedManager()
class var sharedInstance: BandManager {
return _SharedBandManagerInstance
}
override init() {
super.init()
self.clientManager.delegate = self
}
func attachedClients() -> [MSBClient]? {
if let manager = self.clientManager {
self.discoveredClients = [MSBClient]()
for client in manager.attachedClients() {
self.discoveredClients.append(client as! MSBClient)
}
}
return self.discoveredClients
}
func disconnectClient(client: MSBClient) {
if (!client.isDeviceConnected) {
return;
}
if let manager = self.clientManager {
manager.cancelClientConnection(client)
self.client = nil
}
}
func connectClient(client: MSBClient, completion: (connected: Bool) -> Void) {
if (client.isDeviceConnected && self.client == client) {
if (self.connectionBlock != nil)
{
self.connectionBlock!(true)
}
return;
}
if let connectedClient = self.client {
self.disconnectClient(client)
}
self.connectionBlock = completion;
self.clientManager.connectClient(client)
}
func clientManager(clientManager: MSBClientManager!, clientDidConnect client: MSBClient!) {
if (self.connectionBlock != nil) {
self.client = client
self.connectionBlock!(true)
self.connectionBlock = nil
}
self.fireClientChangeNotification(client)
}
func clientManager(clientManager: MSBClientManager!, clientDidDisconnect client: MSBClient!) {
self.fireClientChangeNotification(client)
}
func clientManager(clientManager: MSBClientManager!, client: MSBClient!, didFailToConnectWithError error: NSError!) {
if error != nil {
println(error)
}
NSNotificationCenter.defaultCenter().postNotificationName(kConnectionFailedNotification, object: self, userInfo: ["client": client])
}
func fireClientChangeNotification(client: MSBClient) {
NSNotificationCenter.defaultCenter().postNotificationName(kConnectionChangedNotification, object: self, userInfo: ["client": client])
}
}

Related

Missing argument for parameter 'delegate' in call // JSON file corrupted in SWIFT?

I am following an e-class tutorial for SWIFT // XCODE 11.4 and I have to fetch data from Open Weather API and display it on the interface where people can type in a city and the view controller will display temperature, cloud icon, and description.
Clima App Tutorial
I am using the MVC pattern design and the delegate design to accomplish this tutorial. My swift files are as followed:
Swift Files in MVC Design Pattern
Here are the codes in each of the important files:
I. Model folder
WeatherManager.swift
protocol WeatherManagerDelegate {
func didUpdateWeather(weather: WeatherModel)
}
struct WeatherManager {
let weatherURL = "https://api.openweathermap.org/d.5/weather?appid=c8b50079338280b47a65dd6082551e3b&units=imperial"
let delegate: WeatherManagerDelegate?
func fetchWeather(cityName: String) {
let urlString = "\(weatherURL)&q=\(cityName)"
performRequest(urlString: urlString)
}
func performRequest(urlString: String) {
//create a URL
if let url = URL(string: urlString) {
//create a URLSession
let session = URLSession(configuration: .default)
//give session a task
let task = session.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error!)
return //exit out of the func if there is an error
}
if let safeData = data {
if let weather = self.parseJSON(weatherData: safeData) {
self.delegate?.didUpdateWeather(weather: weather)
}
}
}
//start the tast
task.resume()
}
}
func parseJSON (weatherData: Data) -> WeatherModel? {
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode(WeatherData.self, from: weatherData)
let id = decodedData.weather[0].id
let temp = decodedData.main.temp
let name = decodedData.name
let weather = WeatherModel(conditionId: id, cityName: name, temperature: temp)
return weather
} catch {
print(error)
return nil
}
}
}
WeatherData.swift
struct WeatherData: Codable {
let name: String
let main: Main
let weather: [Weather]
}
struct Main: Codable {
let temp: Double
}
struct Weather: Codable {
let id: Int
}
WeatherModel.swift
struct WeatherModel {
let conditionId: Int
let cityName: String
let temperature: Double
var temperatureString: String {
return String(format: "%.1f", temperature)
}
var conditionName: String {
switch conditionId {
case 200...232:
return "cloud.bolt"
case 300...321:
return "cloud.drizzle"
case 500...531:
return "cloud.rain"
case 600...622:
return "cloud.snow"
case 701...781:
return "cloud.fog"
case 800:
return "sun.max"
case 801...804:
return "cloud.bolt"
default:
return "cloud"
}
}
}
II. Controller
WeatherViewController.swift (place where the error is)
class WeatherViewController: UIViewController, UITextFieldDelegate, WeatherManagerDelegate {
#IBOutlet weak var conditionImageView: UIImageView!
#IBOutlet weak var temperatureLabel: UILabel!
#IBOutlet weak var cityLabel: UILabel!
#IBOutlet weak var searchTextField: UITextField!
var weatherManager = WeatherManager()
override func viewDidLoad() {
super.viewDidLoad()
weatherManager.delegate = self
searchTextField.delegate = self
}
#IBAction func searchPressed(_ sender: UIButton) {
searchTextField.endEditing(true)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
searchTextField.endEditing(true)
print(searchTextField.text!)
return true
}
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
if textField.text != "" {
return true
} else {
textField.placeholder = "Type something..."
return false
}
}
func textFieldDidEndEditing(_ textField: UITextField) {
if let city = searchTextField.text {
weatherManager.fetchWeather(cityname: city)
}
searchTextField.text = ""
}
func didUpdateWeather(weather: WeatherModel) {
print(weather.temperature)
}
}
Here is the error message: Missing argument for parameter 'delegate' in call
Error message in WeatherViewControl.swift
And when I hit the run button, I also got this error in the debug console:
dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around character 0." UserInfo={NSDebugDescription=Invalid value around character 0.})))
Error in the debug console
What should I do to get rid of these errors?
You need to change the keyword "let" to "var" in let delegate: WeatherManagerDelegate? in struct WeatherManager.
Missing argument for parameter 'delegate' in call
When a struct is create a value for each property is required.
If each property is specified with a default value and there is no user-defined initializer then Swift will create a default initializer for the struct.
If there is at least one property without a default value and there is no user-defined initializer then Swift will create a memberwise initializer which has one parameter for each property without a default value.
For example your type:
struct WeatherModel {
let conditionId: Int
let cityName: String
let temperature: Double
...
has three properties without default values. If you start typing:
let myWeatherModel = WeatherModel(
and then take the completion offered you will get (something like):
let wm = WeatherModel(conditionId: <Int>, cityName: <String>, temperature: <Double>)
The completion is showing the memberwise initializer.
Your type which produces the error is:
struct WeatherManager {
let weatherURL = "https://api.openweathermap.org/d.5/weather?appid=c8b50079338280b47a65dd6082551e3b&units=imperial"
let delegate: WeatherManagerDelegate?
which has two properties only one of which has a default value, and it has no initializers, so Swift will create a member wise initialiser automatically.
There is nothing wrong so far.
The line producing the error is:
var weatherManager = WeatherManager()
Here you are attempting to create a WeatherManager without invoking the member wise initalizer, so Swift gives you the error message.
If you click on the error message itself you will see a Fix is offered, click that and Swift will change your code to:
var weatherManager = WeatherManager(delegate: <WeatherManagerDelegate?>)
Select the <WeatherManagerDelegate?> and type the value you wish to pass.
HTH

Getting SwiftUI wrapper of AVPlayer to pause when view disappears

TL;DR
Can't seem to use binding to tell wrapped AVPlayer to stop — why not? The "one weird trick" from Vlad works for me, without state & binding, but why?
See Also
My question is something like this one but that poster wanted to wrap an AVPlayerViewController and I want to control playback programmatically.
This guy also wondered when updateUIView() was called.
What happens (Console logs shown below.)
With code as shown here,
The user taps "Go to Movie"
MovieView appears and the vid plays
This is because updateUIView(_:context:) is being called
The user taps "Go back Home"
HomeView reappears
Playback halts
Again updateUIView is being called.
See Console Log 1
But... remove the ### line, and
Playback continues even when the home view returns
updateUIView is called on arrival but not departure
See Console log 2
If you uncomment the %%% code (and comment out what precedes it)
You get code I thought was logically and idiomatically correct SwiftUI...
...but "it doesn't work". I.e. the vid plays on arrival but continues on departure.
See Console log 3
The code
I do use an #EnvironmentObject so there is some sharing of state going on.
Main content view (nothing controversial here):
struct HomeView: View {
#EnvironmentObject var router: ViewRouter
var body: some View {
ZStack() { // +++ Weird trick ### fails if this is Group(). Wtf?
if router.page == .home {
Button(action: { self.router.page = .movie }) {
Text("Go to Movie")
}
} else if router.page == .movie {
MovieView()
}
}
}
}
which uses one of these (still routine declarative SwiftUI):
struct MovieView: View {
#EnvironmentObject var router: ViewRouter
// #State private var isPlaying: Bool = false // %%%
var body: some View {
VStack() {
PlayerView()
// PlayerView(isPlaying: $isPlaying) // %%%
Button(action: { self.router.page = .home }) {
Text("Go back Home")
}
}.onAppear {
print("> onAppear()")
self.router.isPlayingAV = true
// self.isPlaying = true // %%%
print("< onAppear()")
}.onDisappear {
print("> onDisappear()")
self.router.isPlayingAV = false
// self.isPlaying = false // %%%
print("< onDisappear()")
}
}
}
Now we get into the AVKit-specific stuff. I use the approach described by Chris Mash.
The aforementioned PlayerView, the wrappER:
struct PlayerView: UIViewRepresentable {
#EnvironmentObject var router: ViewRouter
// #Binding var isPlaying: Bool // %%%
private var myUrl : URL? { Bundle.main.url(forResource: "myVid", withExtension: "mp4") }
func makeUIView(context: Context) -> PlayerView {
PlayerUIView(frame: .zero , url : myUrl)
}
// ### This one weird trick makes OS call updateUIView when view is disappearing.
class DummyClass { } ; let x = DummyClass()
func updateUIView(_ v: PlayerView, context: UIViewRepresentableContext<PlayerView>) {
print("> updateUIView()")
print(" router.isPlayingAV = \(router.isPlayingAV)")
// print(" isPlaying = \(isPlaying)") // %%%
// This does work. But *only* with the Dummy code ### included.
// See also +++ comment in HomeView
if router.isPlayingAV { v.player?.pause() }
else { v.player?.play() }
// This logic looks reversed, but is correct.
// If it's the other way around, vid never plays. Try it!
// if isPlaying { v?.player?.play() } // %%%
// else { v?.player?.pause() } // %%%
print("< updateUIView()")
}
}
And the wrappED UIView:
class PlayerUIView: UIView {
private let playerLayer = AVPlayerLayer()
var player: AVPlayer?
init(frame: CGRect, url: URL?) {
super.init(frame: frame)
guard let u = url else { return }
self.player = AVPlayer(url: u)
self.playerLayer.player = player
self.layer.addSublayer(playerLayer)
}
override func layoutSubviews() {
super.layoutSubviews()
playerLayer.frame = bounds
}
required init?(coder: NSCoder) { fatalError("not implemented") }
}
And of course the view router, based on the Blckbirds example
class ViewRouter : ObservableObject {
let objectWillChange = PassthroughSubject<ViewRouter, Never>()
enum Page { case home, movie }
var page = Page.home { didSet { objectWillChange.send(self) } }
// Claim: App will never play more than one vid at a time.
var isPlayingAV = false // No didSet necessary.
}
Console Logs
Console log 1 (playing stops as desired)
> updateUIView() // First call
router.isPlayingAV = false // Vid is not playing => play it.
< updateUIView()
> onAppear()
< onAppear()
> updateUIView() // Second call
router.isPlayingAV = true // Vid is playing => pause it.
< updateUIView()
> onDisappear() // After the fact, we clear
< onDisappear() // the isPlayingAV flag.
Console log 2 (weird trick disabled; playing continues)
> updateUIView() // First call
router.isPlayingAV = false
< updateUIView()
> onAppear()
< onAppear()
// No second call.
> onDisappear()
< onDisappear()
Console log 3 (attempt to use state & binding; playing continues)
> updateUIView()
isPlaying = false
< updateUIView()
> onAppear()
< onAppear()
> updateUIView()
isPlaying = true
< updateUIView()
> updateUIView()
isPlaying = true
< updateUIView()
> onDisappear()
< onDisappear()
Well... on
}.onDisappear {
print("> onDisappear()")
self.router.isPlayingAV = false
print("< onDisappear()")
}
this is called after view is removed (it is like didRemoveFromSuperview, not will...), so I don't see anything bad/wrong/unexpected in that subviews (or even it itself) is not updated (in this case updateUIView)... I would rather surprise if it would be so (why update view, which is not in view hierarchy?!).
So this
class DummyClass { } ; let x = DummyClass()
is rather some wild bug, or ... bug. Forget about it and never use such stuff in releasing products.
OK, one would now ask, how to do with this? The main issue I see here is design-originated, specifically tight-coupling of model and view in PlayerUIView and, as a result, impossibility to manage workflow. AVPlayer here is not part of view - it is model and depending on its states AVPlayerLayer draws content. Thus the solution is to tear apart those entities and manage separately: views by views, models by models.
Here is a demo of modified & simplified approach, which behaves as expected (w/o weird stuff and w/o Group/ZStack limitations), and it can be easily extended or improved (in model/viewmodel layer)
Tested with Xcode 11.2 / iOS 13.2
Complete module code (can be copy-pasted in ContentView.swift in project from template)
import SwiftUI
import Combine
import AVKit
struct MovieView: View {
#EnvironmentObject var router: ViewRouter
// just for demo, but can be interchangable/modifiable
let playerModel = PlayerViewModel(url: Bundle.main.url(forResource: "myVid", withExtension: "mp4")!)
var body: some View {
VStack() {
PlayerView(viewModel: playerModel)
Button(action: { self.router.page = .home }) {
Text("Go back Home")
}
}.onAppear {
self.playerModel.player?.play() // << changes state of player, ie model
}.onDisappear {
self.playerModel.player?.pause() // << changes state of player, ie model
}
}
}
class PlayerViewModel: ObservableObject {
#Published var player: AVPlayer? // can be changable depending on modified URL, etc.
init(url: URL) {
self.player = AVPlayer(url: url)
}
}
struct PlayerView: UIViewRepresentable { // just thing wrapper, as intended
var viewModel: PlayerViewModel
func makeUIView(context: Context) -> PlayerUIView {
PlayerUIView(frame: .zero , player: viewModel.player) // if needed viewModel can be passed completely
}
func updateUIView(_ v: PlayerUIView, context: UIViewRepresentableContext<PlayerView>) {
}
}
class ViewRouter : ObservableObject {
enum Page { case home, movie }
#Published var page = Page.home // used native publisher
}
class PlayerUIView: UIView {
private let playerLayer = AVPlayerLayer()
var player: AVPlayer?
init(frame: CGRect, player: AVPlayer?) { // player is a model so inject it here
super.init(frame: frame)
self.player = player
self.playerLayer.player = player
self.layer.addSublayer(playerLayer)
}
override func layoutSubviews() {
super.layoutSubviews()
playerLayer.frame = bounds
}
required init?(coder: NSCoder) { fatalError("not implemented") }
}
struct ContentView: View {
#EnvironmentObject var router: ViewRouter
var body: some View {
Group {
if router.page == .home {
Button(action: { self.router.page = .movie }) {
Text("Go to Movie")
}
} else if router.page == .movie {
MovieView()
}
}
}
}

Back button not being called in TabbarCoordinator in horizontal flow iOS 12

Coordinator pattern is an old topic with many libraries trying to solve it and I am learning it in simple example app.
My current set up is 3 rootViewControlers: LoadingStateCoordinator, WelcomeCoordinator, TabBarCoordinator but missing connection between UIKit and coordinators. I am trying to implement it with a UINavigationController but the button is not being called. I need a way to connect to back button and a reusable coordinator that I could push to and dellocate accordingly (that is without RxSwift).*Set up Welcome screen as the parent/main navigation and always be able to come back to it.**
So after user selects a form from modal view (vertical flow) presented I show on a push a TabBarCoordinator (horizontal). All viewControllers have empty.storyboard, UIViewController and Coordinator exept the TabBar.Here I only have a coordinator due to the set up of child tab coordinators and the magic needs to happen on a back button tap. Currenly this only being called when user comes from LoadingStateCoordinator. There I need to send the user back to the Welcome screen so they can change the onboarding set up. Here is the first code for LoadingStateCoordinator:
final class LoadingStateCoordinator: NSObject, Coordinator {
*// MARK: - Inputs required*
var childCoordinators: [Coordinator]
var presenter: UINavigationController
private let window: UIWindow
*// MARK: - Initialization*
init(window: UIWindow) {
self.window = window
childCoordinators = []
presenter = UINavigationController()
}
*// MARK: - Coordinator*
func start() {
let controller: LoadingStateViewController = LoadingStateViewController.instantiate()
window.rootViewController = controller
controller.delegate = self
}
}
*// MARK: - LoadingViewControllerDelegate*
extension LoadingStateCoordinator : LoadingViewControllerDelegate {
func performScreenSwitch() {
if UserDefaults.standard.userWasHere == false {
let tabCoordinator: TabBarCoordinator = TabBarCoordinator(window: window, tabBarController: UITabBarController())
window.rootViewController = presenter
addChildCoordinator(tabCoordinator)
tabCoordinator.start()
presenter.pushViewController(tabCoordinator.tabBarController!, animated: true)
} else {
let welcomeCoordinator = WelcomeCoordinator(window: window, presenter: presenter)
window.rootViewController = welcomeCoordinator.presenter
addChildCoordinator(welcomeCoordinator)
welcomeCoordinator.start()
}
}
}
And here is the TabBarCoordinator that need to perform back to Welcome screen action. When I present popToRootfunction it pushes the Welcome screen but all the button there are disbled. I guess to be retain cycle issue. Do I need funadametally another set up? Is there a way to popToRoot(vc) in this set up? What I tryed ended with runtime error "poping to non existing controller".
TabBarCoordinator code that need to perform this:
final class TabBarCoordinator: NSObject, Coordinator {
internal var presenter: UINavigationController
internal var tabBarController: UITabBarController?
internal var childCoordinators: [Coordinator]
var parentCoordinator: LoadingStateCoordinator?
lazy var leftBtn: UIBarButtonItem = {
let button = UIButton(type: .system)
button.setImage(UIImage(systemName: "arrow.turn.up.left"), for: .normal)
button.sizeToFit()
button.addTarget(self,
action: #selector(self.popToRoot(_:)),
for: .touchUpInside)
return UIBarButtonItem(customView: button)
}()
init(window: UIWindow, tabBarController: UITabBarController) {
self.tabBarController = tabBarController
childCoordinators = []
self.presenter = UINavigationController()
}
func start() {
performGetTabBar()
self.presenter.delegate = self
}
private func performGetTabBar() {
let coordinators: [Coordinator] = generateTabCoordinators()
coordinators.forEach({ coordinator in
coordinator.start()
addChildCoordinator(coordinator)
})
let presenters: [UIViewController] = coordinators.map({ coordinator -> UIViewController in
return coordinator.presenter
})
leftBtn.style = .plain
tabBarController?.navigationItem.leftBarButtonItem = leftBtn
tabBarController?.setViewControllers(presenters, animated: false)
selectTab(type: SurfTripCoordinator.self)
}
private func generateTabCoordinators() -> [Coordinator] {
let calculatorCoordinator: CalculatorCoordinator = CalculatorCoordinator(presenter: UINavigationController())
let tripCoordinator: SurfTripCoordinator = SurfTripCoordinator(presenter: UINavigationController())
let sellCoordinator: SavedTripsCoordinator = SavedTripsCoordinator(presenter: UINavigationController())
return [calculatorCoordinator, tripCoordinator, sellCoordinator]
}
*//this is not being called when coming from vertical flow*
#objc func popToRoot(_ sender: UIBarButtonItem) {
let storyboard: UIStoryboard = UIStoryboard(name: Constants.Storyboards.welcomeViewCoordinator, bundle: nil)
let controller: WelcomeViewController = WelcomeViewController.instantiate(from: storyboard)
tabBarController?.navigationController?.pushViewController(controller, animated: true)
}
}
extension TabBarCoordinator: UINavigationControllerDelegate {
func selectTab<T: Coordinator>(type _: T.Type) {
guard let index = childCoordinators.firstIndex(where: { coordinator in
coordinator is T
}) else {
return
}
tabBarController?.selectedIndex = index
}
}
and here is the current WelcomeCoordinator set up
class WelcomeCoordinator: NSObject, Coordinator {
internal var presenter: UINavigationController
var childCoordinators: [Coordinator]
init(window: UIWindow, presenter: UINavigationController) {
self.presenter = presenter
childCoordinators = []
}
func start() {
let storyboard: UIStoryboard = UIStoryboard(name: Constants.Storyboards.welcomeViewCoordinator, bundle: nil)
let controller: WelcomeViewController = WelcomeViewController.instantiate(from: storyboard)
controller.delegate = self
presenter.pushViewController(controller, animated: true)
}
}
extension WelcomeCoordinator : WelcomeViewControllerDelegate {
func performAddLevel() {
let addLevelCoordinator: AddLevelViewCoordinator = AddLevelViewCoordinator(presenter: UINavigationController())
addLevelCoordinator.start()
addChildCoordinator(addLevelCoordinator)
addLevelCoordinator.presenter.modalPresentationStyle = .fullScreen
presenter.present(addLevelCoordinator.presenter, animated: true, completion: nil)
}
}
sorry for the long post I wish there was more reaktive native way to do this...
Ok so I found partlly a solution the back button solution for my case: not using pushViewController or show because it comes with back button. presenter.setViewControllers([tabCoordinator.tabBarController!], animated: true) and there setting the navBar to hidden. I made my own navItem button to navigate to rootVC. Next step to allocate and remove all child tabBar coordinators on back tap recognized.

swift NSURLConnetction not working

I am new to the swift. Just couldn't get the below code working on the http request. Other than printing "start...", it doesn't print anything else. All the connection methods seem not called at all? Any suggestions?
class Network {
var data: NSMutableData = NSMutableData()
func getAcctSummary() {
let urlAcctSummary = "http://www.google.com"
var url: NSURL = NSURL(string: urlAcctSummary)
var request: NSURLRequest = NSURLRequest(URL: url)
var connection: NSURLConnection = NSURLConnection(request: request, delegate: self, startImmediately: false)
connection.start()
println("started...")
}//getAcctSummary
func connection(connection: NSURLConnection!, didFailWithError error: NSError!) {
println("Failed with error:\(error.localizedDescription)")
}
func connection(didReceiveResponse: NSURLConnection!, didReceiveResponse response: NSURLResponse!) {
println("didReceiveResponse")
}
func connection(connection: NSURLConnection!, didReceiveData conData: NSData!) {
self.data.appendData(conData)
println("here 1")
}
func connectionDidFinishLoading(connection: NSURLConnection!) {
println(self.data)
println("here")
}
}
In order to determine which optional protocol methods are supported, the NSURLConnection delegate is required to be a subclass of NSObject. Change the first line to:
class Network : NSObject {
And you should be good-to-go
Try using swift NSURLSession. Worked best for me here is some ex code but had to pull out my custom code.
func someFunc() ->()
{
var session = NSURLSession.sharedSession()
var personURL = "URL"
let url = NSURL(string:personURL)
var task:NSURLSessionDataTask = session.dataTaskWithURL(url, completionHandler:apiHandler)
task.resume()
}
func apiHandler(data:NSData!, response:NSURLResponse!, error:NSError!)
{
if error
{
println("API error: \(error), \(error.userInfo)")
}
var jsonError:NSError?
var json:JMSMediaDictionary = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &jsonError) as Dictionary
println(json)
//do stuff with data
}

Adding observer for KVO without pointers using Swift

In Objective-C, I would normally use something like this:
static NSString *kViewTransformChanged = #"view transform changed";
// or
static const void *kViewTransformChanged = &kViewTransformChanged;
[clearContentView addObserver:self
forKeyPath:#"transform"
options:NSKeyValueObservingOptionNew
context:&kViewTransformChanged];
I have two overloaded methods to choose from to add an observer for KVO with the only difference being the context argument:
clearContentView.addObserver(observer: NSObject?, forKeyPath: String?, options: NSKeyValueObservingOptions, context: CMutableVoidPointer)
clearContentView.addObserver(observer: NSObject?, forKeyPath: String?, options: NSKeyValueObservingOptions, kvoContext: KVOContext)
With Swift not using pointers, I'm not sure how to dereference a pointer to use the first method.
If I create my own KVOContext constant for use with the second method, I wind up with it asking for this:
let test:KVOContext = KVOContext.fromVoidContext(context: CMutableVoidPointer)
EDIT: What is the difference between CMutableVoidPointer and KVOContext? Can someone give me an example how how to use them both and when I would use one over the other?
EDIT #2: A dev at Apple just posted this to the forums: KVOContext is going away; using a global reference as your context is the way to go right now.
There is now a technique officially recommended in the documentation, which is to create a private mutable variable and use its address as the context.
(Updated for Swift 3 on 2017-01-09)
// Set up non-zero-sized storage. We don't intend to mutate this variable,
// but it needs to be `var` so we can pass its address in as UnsafeMutablePointer.
private static var myContext = 0
// NOTE: `static` is not necessary if you want it to be a global variable
observee.addObserver(self, forKeyPath: …, options: [], context: &MyClass.myContext)
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
if context == &myContext {
…
}
else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
}
}
Now that KVOContext is gone in Xcode 6 beta 3, you can do the following. Define a global (i.e. not a class property) like so:
let myContext = UnsafePointer<()>()
Add an observer:
observee.addObserver(observer, forKeyPath: …, options: nil, context: myContext)
In the observer:
override func observeValueForKeyPath(keyPath: String!, ofObject object: AnyObject!, change: [NSObject : AnyObject]!, context: UnsafePointer<()>) {
if context == myContext {
…
} else {
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
}
}
Swift 4 - observing contentSize change on UITableViewController popover to fix incorrect size
I had been searching for an answer to change to a block based KVO because I was getting a swiftlint warning and it took me piecing quite a few different answers together to get to the right solution. Swiftlint warning:
Block Based KVO Violation: Prefer the new block based KVO API with keypaths when using Swift 3.2 or later. (block_based_kvo).
My use case was to present a popover controller attached to a button in a Nav bar in a view controller and then resize the popover once it's showing - otherwise it would be too big and not fitting the contents of the popover. The popover itself was a UITableViewController that contained static cells, and it was displayed via a Storyboard segue with style popover.
To setup the block based observer, you need the following code inside your popover UITableViewController:
// class level variable to store the statusObserver
private var statusObserver: NSKeyValueObservation?
// Create the observer inside viewWillAppear
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
statusObserver = tableView.observe(\UITableView.contentSize,
changeHandler: { [ weak self ] (theTableView, _) in self?.popoverPresentationController?.presentedViewController.preferredContentSize = theTableView.contentSize
})
}
// Don't forget to remove the observer when the popover is dismissed.
override func viewDidDisappear(_ animated: Bool) {
if let observer = statusObserver {
observer.invalidate()
statusObserver = nil
}
super.viewDidDisappear(animated)
}
I didn't need the previous value when the observer was triggered, so left out the options: [.new, .old] when creating the observer.
Update for Swift 4
Context is not required for block-based observer function and existing #keyPath() syntax is replaced with smart keypath to achieve swift type safety.
class EventOvserverDemo {
var statusObserver:NSKeyValueObservation?
var objectToObserve:UIView?
func registerAddObserver() -> Void {
statusObserver = objectToObserve?.observe(\UIView.tag, options: [.new, .old], changeHandler: {[weak self] (player, change) in
if let tag = change.newValue {
// observed changed value and do the task here on change.
}
})
}
func unregisterObserver() -> Void {
if let sObserver = statusObserver {
sObserver.invalidate()
statusObserver = nil
}
}
}
Complete example using Swift:
//
// AppDelegate.swift
// Photos-MediaFramework-swift
//
// Created by Phurg on 11/11/16.
//
// Displays URLs for all photos in Photos Library
//
// #see http://stackoverflow.com/questions/30144547/programmatic-access-to-the-photos-library-on-mac-os-x-photokit-photos-framewo
//
import Cocoa
import MediaLibrary
// For KVO: https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html#//apple_ref/doc/uid/TP40014216-CH7-ID12
private var mediaLibraryLoaded = 1
private var rootMediaGroupLoaded = 2
private var mediaObjectsLoaded = 3
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
#IBOutlet weak var window: NSWindow!
var mediaLibrary : MLMediaLibrary!
var allPhotosAlbum : MLMediaGroup!
func applicationDidFinishLaunching(_ aNotification: Notification) {
NSLog("applicationDidFinishLaunching:");
let options:[String:Any] = [
MLMediaLoadSourceTypesKey: MLMediaSourceType.image.rawValue, // Can't be Swift enum
MLMediaLoadIncludeSourcesKey: [MLMediaSourcePhotosIdentifier], // Array
]
self.mediaLibrary = MLMediaLibrary(options:options)
NSLog("applicationDidFinishLaunching: mediaLibrary=%#", self.mediaLibrary);
self.mediaLibrary.addObserver(self, forKeyPath:"mediaSources", options:[], context:&mediaLibraryLoaded)
NSLog("applicationDidFinishLaunching: added mediaSources observer");
// Force load
self.mediaLibrary.mediaSources?[MLMediaSourcePhotosIdentifier]
NSLog("applicationDidFinishLaunching: done");
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
NSLog("observeValue: keyPath=%#", keyPath!)
let mediaSource:MLMediaSource = self.mediaLibrary.mediaSources![MLMediaSourcePhotosIdentifier]!
if (context == &mediaLibraryLoaded) {
NSLog("observeValue: mediaLibraryLoaded")
mediaSource.addObserver(self, forKeyPath:"rootMediaGroup", options:[], context:&rootMediaGroupLoaded)
// Force load
mediaSource.rootMediaGroup
} else if (context == &rootMediaGroupLoaded) {
NSLog("observeValue: rootMediaGroupLoaded")
let albums:MLMediaGroup = mediaSource.mediaGroup(forIdentifier:"TopLevelAlbums")!
for album in albums.childGroups! {
let albumIdentifier:String = album.attributes["identifier"] as! String
if (albumIdentifier == "allPhotosAlbum") {
self.allPhotosAlbum = album
album.addObserver(self, forKeyPath:"mediaObjects", options:[], context:&mediaObjectsLoaded)
// Force load
album.mediaObjects
}
}
} else if (context == &mediaObjectsLoaded) {
NSLog("observeValue: mediaObjectsLoaded")
let mediaObjects:[MLMediaObject] = self.allPhotosAlbum.mediaObjects!
for mediaObject in mediaObjects {
let url:URL? = mediaObject.url
// URL does not extend NSObject, so can't be passed to NSLog; use string interpolation
NSLog("%#", "\(url)")
}
}
}
}

Resources