WKExtensionsDelegateClassName is Invalid in info.plist - watchkit

So I am banging my head, I realized my stand along Watch App had a STUPID long name of "App Name - WatchKit App" so I went into my Target and changed the Display Name to "App Name" removing WatchKit App. Well now my app won't validate when uploading to the Appstore. I get the message - Invalid Info.plist key. The key WKExtensionDelegateClassName in bundle App Name.app/Watch/App Name WatchKit App.app is invalid.
My Info.plist has the value of
<key>WKExtensionDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).ExtensionDelegate</string>
I have confirmed that I have #WKExtensionDelegateAdaptor(ExtensionDelegate.self) var delegate in my #main for the SwiftUI App. And when I print a few values in my app launch I get the following confirmations:
Super Init - ExtensionDelegate
Contentview
applicationDidFinishLaunching for watchOS
Super Init - ExtensionDelegate
Optional(Wasted_Time_Watch_Extension.MeetingSetup)
Optional(Wasted_Time_Watch_Extension.MeetingStatistics)
Optional(Wasted_Time_Watch_Extension.Meeting)
applicationDidBecomeActive for watchOS
update complication
I create three classes at launch and print this in the log with print(ExtensionDelegate.shared.Setup as Any) , etc. The other lines are just confirming where I am at app startup.
This is a WatchOS8 application and I am running Xcode version Version 13.1 (13A1030d).
Update - Here's the entry in my plist
<key>WKExtensionDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).ExtensionDelegate</string>
<key>WKWatchOnly</key>
And my App code
import SwiftUI
#if os(watchOS)
import ClockKit
#endif
struct DelegateKey: EnvironmentKey {
typealias Value = ExtensionDelegate
static let defaultValue: ExtensionDelegate = ExtensionDelegate()
}
extension EnvironmentValues {
var extensionDelegate: DelegateKey.Value {
get {
return self[DelegateKey.self]
}
set {
self[DelegateKey.self] = newValue
}
}
}
#main
struct WastedTimeWatchApp: App {
#WKExtensionDelegateAdaptor(ExtensionDelegate.self) var delegate
let prefs: UserDefaults = UserDefaults(suiteName: suiteName)!
#SceneBuilder var body: some Scene {
WindowGroup {
NavigationView {
ContentView()
.environment(\.extensionDelegate, delegate)
}
}
}
}
class ExtensionDelegate: NSObject, WKExtensionDelegate, ObservableObject {
#Environment(\.extensionDelegate) static var shared
// variables removed to simplify posting
override init() {
print("Super Init - ExtensionDelegate")
super.init()
}
func applicationDidFinishLaunching() {
print("applicationDidFinishLaunching for watchOS")
ExtensionDelegate.shared.meetingSetup = MeetingSetup()
print(ExtensionDelegate.shared.meetingSetup as Any)
ExtensionDelegate.shared.meetingStatistics = MeetingStatistics()
print(ExtensionDelegate.shared.meetingStatistics as Any)
ExtensionDelegate.shared.meeting = Meeting()
print(ExtensionDelegate.shared.meeting as Any)
}
func applicationDidBecomeActive() {
print("applicationDidBecomeActive for watchOS")
print("update complication")
let server = CLKComplicationServer.sharedInstance()
for complication in server.activeComplications ?? [] {
server.reloadTimeline(for: complication)
}
}
func applicationDidBecomeInactive() {
print("update complication")
let server = CLKComplicationServer.sharedInstance()
for complication in server.activeComplications ?? [] {
server.reloadTimeline(for: complication)
}
print("applicationDidBecomeInactive for watchOS")
}
}

I figured this out... I had duplicated the plist entry in both the WatchKit App and WatchKit Extension plist file. Removed it from the list WatchKit Extension plist and all is working fine.

Related

SwiftUI Navigation automatically closing/pop - Realm

I'm populating a List with a Realm Result set.
When navigating from this list it opens a new view then automatically closes that view.
Using a struct presents no issue.
Why would the second view automatically close?
I have a screen recording but cant post here.
import SwiftUI
import Combine
struct TestStruct:Identifiable{
let id = UUID()
let firstname: String
}
extension TestStruct {
static func all() -> [TestStruct]{
return[
TestStruct(firstname: "Joe"),
TestStruct(firstname: "Jane"),
TestStruct(firstname: "Johns")
]
}
}
struct TestListView: View {
let realmList = Horoscope.getHoroscopes() //Fetches from Realm
let structList = TestStruct.all()
var body: some View {
NavigationView{
// This owrks
// List(structList) { item in
// MyItemRow(itemTxt: item.firstname)
// }
//This automatically closes the view
List(realmList) { item in
MyItemRow(itemTxt: item.firstname)
}
.navigationBarTitle("Charts", displayMode: .automatic)
.navigationBarItems(trailing: EditButton())
}
}
}
struct MyItemRow: View {
var itemTxt:String
var body: some View {
NavigationLink(destination: Text("Test")) {
Text(itemTxt)
}
}
}
struct TestListView_Previews: PreviewProvider {
static var previews: some View {
TestListView()
}
}
I think the answer can be found here
In short, do not generate the id of the collection on which the ForEach iterates. It would detect a change and navigate back.
Realm object has an auto generated id property with each reference, try replacing it with a consistent id
The following solution worked for me.
The code with an issue (specifying id: \.self is the root cause since it uses the hash calculated from all objects the Stream object consists of, including the data that lies in a subarray).
...
List(streams, id: \.self) { stream in
...
The code with no issues:
...
List(streams, id: \._id) { stream in
// or even List(streams) { stream in
...
The streams is a #ObservedResults(Stream.self) var streams and the object scheme is:
final class Stream: Object, ObjectKeyIdentifiable {
#Persisted(primaryKey: true) var _id: ObjectId
#Persisted var title: String
#Persisted var subtitle: String?
#Persisted var topics = RealmSwift.List<Topic>()
// tags, etc.
}
The issue happened when I added new topic at the topics list in the first stack of the navigationView.

CFNetwork SSLHandshake failed (-9807)

I keep receiving this error, CFNetwork SSLHandshake failed (-9807), in the debug window and have no data displayed when trying to populate a UITableViewController with Firebase data. I have tried this potential solution iOS 9 ATS and Firebase REST but still have the issue.
The code I am using is (Credit to #DavidEast)
class TableViewController1: UITableViewController {
// your firebase reference as a property
var ref: Firebase!
// your data source, you can replace this with your own model if you wish
var items = [FDataSnapshot]()
override func viewDidLoad() {
super.viewDidLoad()
// initialize the ref in viewDidLoad
ref = Firebase(url:"https://the-lighthouse-app.firebase.io/states")
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
// listen for update with the .Value event
ref.observeEventType(.Value) { (snapshot: FDataSnapshot!) in
var newItems = [FDataSnapshot]()
// loop through the children and append them to the new array
for item in snapshot.children {
newItems.append(item as! FDataSnapshot)
}
// replace the old array
self.items = newItems
// reload the UITableView
self.tableView.reloadData()
}
}
}

Realm EXC_BAD_ACCESS when querying database

Im new to realm. I have read the documentation and followed the examples in creating and querying the database. Whenever I run the code below it crashes on the line
let realm = Realm
I have a view controller that I am trying to query realm like so:
// Get the default Realm
let realm = Realm()
// Query using an NSPredicate
let predicate = NSPredicate(format: "s3_url = %#", s3_url)
let medias = realm.objects(MediaRealm).filter(predicate)
everytime i run this i get Thread1: EXC_BAD_ACCESS
let realm = Realm()
import UIKit
import Alamofire
import RealmSwift
class ViewController: UIViewController {
let mediaCollection = MediaCollection()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
loadInitialData()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func loadInitialData(){
//connect to url
mediaCollection.fetch(onSuccess:{ self.onEventFetched($0) }, onError:{ self.displayAlert($0)})
}
func onEventFetched()->Void{
println("Got stuff")
println(mediaCollection.models.count)
processCollection()
}
func processCollection()->Void{
for media in mediaCollection.models{
let m = media as! Media
println(m.s3_url)
println(m.title)
if let s3_url = m.s3_url{
//query database, if it already exists dont download it
// Get the default Realm
let realm = Realm()
// Query using an NSPredicate
let predicate = NSPredicate(format: "s3_url = %#", s3_url)
let medias = realm.objects(MediaRealm).filter(predicate)
if medias.count == 0 {
println("no files found")
//downloadFile(s3_url)
}
}
}
}
What am I doing wrong?
This was caused by me adding new fields to the database and not running a migration.
Solution was to delete the app and start over.

ViewController does not conform to protocol MSBandClientManagerDelegate

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])
}
}

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