Apple File Application perform "Move" action via file provider extension my extention display disable - ios

In my file provider extension i want to perform move operation in my NoteProvider extension.
I can move any file via Drag operation above any folder it works properly But when i try to "Move" via Action display in bellow screen at that time screen2 display and other extension are enable but my NotProvider Extension was disable.
My code are FileProviderItem
class FileProviderItem: NSObject, NSFileProviderItem {
public var id: String?
public var name: String?
var childItemCount : NSNumber?
var documentSize: NSNumber?
var creationDate : Date?
var contentModificationDate : Date?
var lastUsedDate: Date?
var isDownloaded: Bool = false
public var fTypeIdentifier: String?
var pid : NSFileProviderItemIdentifier!
var parentItemIdentifier: NSFileProviderItemIdentifier {
return pid
}
var typeIdentifier: String {
return fTypeIdentifier! // for folder = "public.folder", for file = file type UTI
}
var itemIdentifier: NSFileProviderItemIdentifier {
return NSFileProviderItemIdentifier(self.id!)
}
var filename: String {
return self.name!
}
override init() {
}
override func isEqual(_ object: Any?) -> Bool {
if let obj = object as? FileProviderItem {
if self.itemIdentifier == obj.itemIdentifier {
return true
}
}
return false
}
var capabilities: NSFileProviderItemCapabilities {
return .allowsAll
}
}
For FileProviderExtension
class FileProviderExtension: NSFileProviderExtension {
override func item(for identifier: NSFileProviderItemIdentifier) throws -> NSFileProviderItem {
// resolve the given identifier to a record in the model
// db = Array of NSFileProviderItem
for i in db {
if i.itemIdentifier.rawValue == identifier.rawValue {
return i
}
}
// TODO: implement the actual lookup
throw NSError(domain: NSCocoaErrorDomain, code: NSNotFound, userInfo:[:])
}
override func importDocument(at fileURL: URL, toParentItemIdentifier parentItemIdentifier: NSFileProviderItemIdentifier, completionHandler: #escaping (NSFileProviderItem?, Error?) -> Void) {
print("importDocument :- \(fileURL) parentItemIdentifier = \(parentItemIdentifier)")
completionHandler(nil, nil)
}
override func reparentItem(withIdentifier itemIdentifier: NSFileProviderItemIdentifier, toParentItemWithIdentifier parentItemIdentifier: NSFileProviderItemIdentifier, newName: String?, completionHandler: #escaping (NSFileProviderItem?, Error?) -> Void) {
print("reparentItem :- \(itemIdentifier) parentItemIdentifier = \(parentItemIdentifier) newName = \(String(describing: newName))")
guard let item = try? item(for: itemIdentifier) as? FileProviderItem else {
completionHandler(nil, NSFileProviderError(.noSuchItem))
return
}
item?.pid = NSFileProviderItemIdentifier(rawValue: parentItemIdentifier.rawValue)
//item?.name = newName
completionHandler(item, nil)
}
}
NoteProvider(FileProvider) .plist file image

return NSFileProviderItem instance in itemForIdentifier method for identifier NSFileProviderRootContainerItemIdentifier. If you return nil for root identifier, app wont be enabled in move operation.

Related

DropInfo in performDrop method doesn't contain my drag object in SwiftUI

I have these extensions to my NSObject, Codable class:
extension SavingsGoal: NSItemProviderWriting {
public static let typeIdentifier = "com.AaronLBratcher.SavingsGoal.Drag"
public static var writableTypeIdentifiersForItemProvider: [String] { [typeIdentifier] }
public typealias DragHandler = (Data?, Error?) -> Void
public func loadData(withTypeIdentifier typeIdentifier: String, forItemProviderCompletionHandler completionHandler: #escaping DragHandler) -> Progress? {
do {
print("^^^ encoding")
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
completionHandler(try encoder.encode(self), nil)
} catch {
completionHandler(nil, error)
}
return nil
}
}
extension SavingsGoal: NSItemProviderReading {
public static var readableTypeIdentifiersForItemProvider: [String] { [typeIdentifier] }
public static func object(withItemProviderData data: Data, typeIdentifier: String) throws -> SavingsGoal {
print("^^^ decoding")
let decoder = JSONDecoder()
return try decoder.decode(SavingsGoal.self, from: data)
}
}
and these onDrag, onDrop method calls on my Views:
.onDrag { NSItemProvider(object: goal) }
.onDrop(of: [SavingsGoal.typeIdentifier], delegate: viewModel)
Visually the drag works, showing a plus when it hits the target View, however only the delegate's performDrop call is made. The loadData and object methods are not called in the extensions and the DropInfo doesn't contain my object.
I have carefully checked and rechecked the code against available examples and cannot find what I did wrong.
Full test project is here: https://github.com/AaronBratcher/DragTest

Creating example of Core Data entity

Creating an example for a struct is very easy and straightforward. For example,
import Foundation
struct User: Identifiable, Codable {
let id: UUID
let isActive: Bool
let name: String
let age: Int
let company: String
static let example = User(id: UUID(), isActive: true, name: "Rick Owens", age: 35, company: "Rick Owens Inc.")
}
Now, how can I create an example if I made this an entity in core data? I can't just put let example = CachedUser(id: UUID(), ...) like I did with the struct. I want this example to be part of my core data automatically without having to manually create it by using forms, buttons, etc... Thanks in advance!
You can simply check if your default user exists in database. If it does not then you need to create one and save it. Something like the following would work if you have synchronous operations:
class CachedUser {
static var example: CachedUser = {
let exampleUUID = UUID(uuidString: "33041937-05b2-464a-98ad-3910cbe0d09e")!
if let existingUser = Database.fetchUser(id: exampleUUID) {
return existingUser
} else {
let newUser = CachedUser()
// TODO: apply example values to user
Database.saveUser(newUser)
return newUser
}
}()
}
This will lazily return existing or generate a new user for you. This user will then be persistent in your database.
The code will only be executed once per session, first time you call CachedUser.example.
If you have your database setup asynchronous then with closures it should look something like this:
class User {
static private(set) var example: User!
static func prepareExampleUser(_ completion: () -> Void) {
let exampleUUID = UUID(uuidString: "33041937-05b2-464a-98ad-3910cbe0d09e")!
Database.fetchUser(id: exampleUUID) { user in
if let user = user {
example = user
completion()
} else {
let newUser = User()
newUser.id = exampleUUID
// TODO: apply example values to user
Database.saveUser(newUser) {
example = newUser
completion()
}
}
}
}
But in this case it makes sense to warmup your application before you show screens that require this user to be present. You can for instance have a loading screen when your app first starts and continue to next screen once this method has finished...
// Loading screen enters
self.startLoading()
User.prepareExampleUser {
self.navigateToNextScreen()
self.stopLoading()
}
In both cases you now hold a static property to your example entry such as User.example which can be used anywhere.
But in both cases you may stumble to issue if user (if able to) deletes this entry from database. You would need to handle that case. Either prevent that action or create a new example user once the old one is deleted.
To access this manager put
let mgr = CachedUserPersistenceManager()
In a ViewModel or a View
/// Manager for the Item entity
class CachedUserPersistenceManager: PersistenceManager<CachedUser>{
let sampleUUID = UUID(uuidString: "00000000-0000-0000-0000-000000000000")!
init(isTest: Bool = false) {
super.init(entityType: CachedUser.self, isTest: isTest)
//Preloads the user
preloadSample()
}
///Preloads a sample object to the context
func preloadSample(){
let list = retrieveObjects(sortDescriptors: nil, predicate: NSPredicate(format: "%K == %#", #keyPath(CachedUser.uuid), sampleUUID as CVarArg)
)
if list.isEmpty{
let sampleItem = createObject()
sampleItem.uuid = sampleUUID
save()
}
}
override func addSample() -> CachedUser {
let new = super.addSample() as CachedUser
//add any sample code
return new
}
override func createObject() -> CachedUser {
super.createObject()!
}
override func updateObject(object: CachedUser) -> Bool {
//Replace the uuid if needed
if object.uuid == sampleUUID{
object.uuid = UUID()
}
return super.updateObject(object: object)
}
}
The generic classes that are a part of this code are below. You don't need them per say it just makes some of the code reusable through the app.
//Manager for any Entity
class PersistenceManager<T : NSManagedObject>{
let serviceSD: CoreDataPersistenceService<T>
internal init(entityType: T.Type, isTest: Bool = false) {
self.serviceSD = CoreDataPersistenceService(isTest: isTest, entityType: entityType)
}
//MARK: convenience
func addSample() -> T {
let newItem = createObject()
return newItem!
}
//MARK: Persistence Service Methods
func createObject() -> T? {
let result = serviceSD.createObject()
return result
}
func updateObject(object: T) -> Bool {
return serviceSD.updateObject(object: object)
}
func deleteObject(object: T) -> Bool {
return serviceSD.deleteObject(object: object)
}
func deleteAllObjects(entityName: String, isConfirmed: Bool) -> Bool {
return serviceSD.deleteAllObjects(isConfirmed: isConfirmed)
}
func retrieveObjects(sortDescriptors: [NSSortDescriptor]?, predicate: NSPredicate?) -> [T]{
return serviceSD.retrieveObjects(sortDescriptors: sortDescriptors, predicate: predicate)
}
func retrieveObject(id: String) -> T? {
return serviceSD.retrieveObject(sortDescriptors: nil, id: id).first
}
func resetChanges() {
serviceSD.resetChanges()
}
func save() {
_ = serviceSD.save()
}
}
//Service for Any Entity
class CoreDataPersistenceService<T: NSManagedObject>: NSObject {
var persistenceController: PersistenceController
let entityType: T.Type
required init(isTest: Bool = false, entityType: T.Type) {
if isTest{
self.persistenceController = PersistenceController.preview
}else{
self.persistenceController = PersistenceController.previewAware
}
self.entityType = entityType
super.init()
}
//MARK: CRUD methods
func createObject() -> T? {
let result = entityType.init(context: persistenceController.container.viewContext)
return result
}
func updateObject(object: T) -> Bool {
var result = false
result = save()
return result
}
func deleteObject(object: T) -> Bool {
var result = false
persistenceController.container.viewContext.delete(object)
result = save()
return result
}
func deleteAllObjects(isConfirmed: Bool) -> Bool {
var result = false
//Locked in so only the Generic "Item" can be deleted like this
if entityType == Item.self && isConfirmed == true{
let deleteRequest = NSBatchDeleteRequest(fetchRequest: entityType.fetchRequest())
do {
try persistenceController.container.persistentStoreCoordinator.execute(deleteRequest, with: persistenceController.container.viewContext)
} catch {
print(error)
result = false
}
}
return result
}
func resetChanges() {
persistenceController.container.viewContext.rollback()
_ = save()
}
func save() -> Bool {
var result = false
do {
if persistenceController.container.viewContext.hasChanges{
try persistenceController.container.viewContext.save()
result = true
}else{
result = false
}
} catch {
print(error)
}
return result
}
func retrieveObject(sortDescriptors: [NSSortDescriptor]? = nil, id: String) -> [T]{
return retrieveObjects(sortDescriptors: sortDescriptors, predicate: NSPredicate(format: "id == %#", id))
}
func retrieveObjects(sortDescriptors: [NSSortDescriptor]? = nil, predicate: NSPredicate? = nil) -> [T]
{
let request = entityType.fetchRequest()
if let sortDescriptor = sortDescriptors
{
request.sortDescriptors = sortDescriptor
}
if let predicate = predicate
{
request.predicate = predicate
}
do
{
let results = try persistenceController.container.viewContext.fetch(request)
return results as! [T]
}
catch
{
print(error)
return []
}
}
}
The previewAware variable that is mentioned goes with the Apple standard code in the PersistenceController
It automatically give you the preview container so you don't have to worry about adapting your code for samples in Canvas. Just add the below code to the PersistenceController
static var previewAware : PersistenceController{
if ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" {
return PersistenceController.preview
}else{
return PersistenceController.shared
}
}

AWSMobileHubHelper add User pool as AWSIdentityProvider, how to handle `func login(completionHandler: (AnyObject, NSError) -> Void)`?

I want to add AWS User pool as a AWSIdentityProvider to my iOS Application,
But there is little guide for the AWSMobileHubHelper.
So I've try it by my self.
In my LoginViewController I treat AWS User pool as other provider like this:
final
class LoginViewController: UIViewController {
#IBOutlet weak var usernameTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
#IBOutlet weak var loginButton: UIButton!
var passwordAuthenticationCompletion: AWSTaskCompletionSource!
var completionHandler: ((AnyObject, NSError) -> Void)?
override func viewDidLoad() {
super.viewDidLoad()
AWSFacebookSignInProvider.sharedInstance().setPermissions(["public_profile"]);
AWSGoogleSignInProvider.sharedInstance().setScopes(["profile", "openid"])
AWSGoogleSignInProvider.sharedInstance().setViewControllerForGoogleSignIn(self)
}
#IBAction private func facebookLogin() {
handleLogin(signInProvider: AWSFacebookSignInProvider.sharedInstance())
}
#IBAction private func googleLogin() {
handleLogin(signInProvider: AWSGoogleSignInProvider.sharedInstance())
}
#IBAction private func myLogin() {
handleLogin(signInProvider: LoginProvider.sharedInstance())
}
private func handleLogin(signInProvider signInProvider: AWSSignInProvider) {
title = "Loging ..."
AWSIdentityManager.defaultIdentityManager().loginWithSignInProvider(signInProvider) { (result, error) in
switch error {
case let error? where error.domain != "success":
print("Login failed.")
default:
print("Login succeed.")
}
}
}
}
My LoginProvider code:
final
class LoginProvider: NSObject {
static func sharedInstance() -> LoginProvider {
return _sharedInstance
}
static private let _sharedInstance = LoginProvider()
lazy var pool: AWSCognitoIdentityUserPool = {
let serviceConfiguration = AWSServiceConfiguration(region: .USEast1, credentialsProvider: nil)
let poolConfiguration = AWSCognitoIdentityUserPoolConfiguration(clientId: "clientId", clientSecret: "clientSecret", poolId: "poolId")
AWSCognitoIdentityUserPool.registerCognitoIdentityUserPoolWithConfiguration(serviceConfiguration, userPoolConfiguration: poolConfiguration, forKey: "UserPool")
let result = AWSCognitoIdentityUserPool(forKey: "UserPool")
result.delegate = self
return result
}()
}
extension LoginProvider: AWSCognitoIdentityInteractiveAuthenticationDelegate {
func startPasswordAuthentication() -> AWSCognitoIdentityPasswordAuthentication {
return loginViewController
}
func startMultiFactorAuthentication() -> AWSCognitoIdentityMultiFactorAuthentication {
fatalError("Identity MultiFactor Authentication Not Supportted!")
}
private var loginViewController: LoginViewController {
return LoginViewController.sharedInstance
}
}
extension LoginProvider: AWSIdentityProvider {
var identityProviderName: String {
return pool.identityProviderName
}
func token() -> AWSTask {
return pool.token()
}
}
extension LoginProvider: AWSSignInProvider {
var loggedIn: Bool {
#objc(isLoggedIn) get {
return currentUser?.signedIn ?? false
}
}
var imageURL: NSURL? {
return nil
}
var userName: String? {
return currentUser?.username
}
func login(completionHandler: (AnyObject, NSError) -> Void) {
loginViewController.completionHandler = completionHandler
loginViewController.doLogin()
}
func logout() {
currentUser?.signOut()
}
func reloadSession() {
currentUser?.getSession()
}
func interceptApplication(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {
return true
}
func interceptApplication(application: UIApplication, openURL url: NSURL, sourceApplication: String?, annotation: AnyObject) -> Bool {
return false
}
private var currentUser: AWSCognitoIdentityUser? {
return pool.currentUser()
}
}
I've extended my LoginViewController to support the process:
extension LoginViewController: AWSCognitoIdentityPasswordAuthentication {
func doLogin() {
pool.getUser(usernameTextField.text!).getSession(usernameTextField.text!, password: passwordTextField.text!, validationData: nil).continueWithBlock { task -> AnyObject? in
print("pool.getUser().getSession")
print("task.result:", task.result)
print("task.error:", task.error)
if let session = task.result as? AWSCognitoIdentityUserSession {
print("session.idToken:", session.idToken?.tokenString)
print("session.accessToken:", session.accessToken?.tokenString)
print("session.refreshToken:", session.refreshToken?.tokenString)
print("session.expirationTime:", session.expirationTime)
}
switch (task.result, task.error) {
case let (_, error?):
self.completionHandler?("", error)
self.completionHandler = nil
case let (result?, _):
self.completionHandler?(result, NSError(domain: "success", code: -1, userInfo: nil))
self.completionHandler = nil
default: break
}
return nil
}
}
func getPasswordAuthenticationDetails(authenticationInput: AWSCognitoIdentityPasswordAuthenticationInput, passwordAuthenticationCompletionSource: AWSTaskCompletionSource) {
self.passwordAuthenticationCompletion = passwordAuthenticationCompletionSource
}
func didCompletePasswordAuthenticationStepWithError(error: NSError?) {
switch error {
case let error?:
Queue.Main.execute {
self.completionHandler?("", error)
self.completionHandler = nil
}
default: break
}
}
private var pool: AWSCognitoIdentityUserPool! {
return LoginProvider.sharedInstance().pool
}
}
extension LoginViewController {
#nonobjc static let navigationController = UIStoryboard(name: "Login", bundle: nil).instantiateInitialViewController() as! UINavigationController
static var sharedInstance: LoginViewController {
return navigationController.viewControllers[0] as! LoginViewController
}
}
Now I can login to my user pool, but I can't integrate it with federated id pool, It think I've give a wrong result when use completionHandler.
In LoginViewController's func doLogin() , when i get the getSession()'s result which is AWSCognitoIdentityUserSession, How can I convert it to other result that completionHandler needed?

How can I create a base from this Swift class so that it can be inherited?

I have a class named UserManager.
public class UserManager{
static let sharedInstance = UserManager()
let center = NSNotificationCenter.defaultCenter()
let queue = NSOperationQueue.mainQueue()
var resources = Dictionary<Int, User>()
var clients = Dictionary<Int, Set<String>>()
private init(){
}
private func addToClientMap(id: Int, clientName: String){
if clients[id] == nil {
clients[id] = Set<String>()
clients[id]!.insert(clientName)
}else{
clients[id]!.insert(clientName)
}
}
func getResource(id: Int, clientName: String) -> User?{
if let resource = resources[id] {
addToClientMap(id, clientName: clientName)
return resource
}else{
return nil
}
}
func createResource(data:JSON, clientName: String) -> User? {
if let id = data["id"].int {
if let resource = resources[id] {
addToClientMap(id, clientName: clientName)
return resource
}else{
resources[id] = mapJSONToUser(data) //need to make generic
addToClientMap(id, clientName: clientName)
return resources[id]
}
}
return nil
}
func releaseResource(id: Int, clientName: String){
if clients[id] != nil {
clients[id]!.remove(clientName)
if clients[id]!.count == 0 {
resources.removeValueForKey(id)
clients.removeValueForKey(id)
}
}
}
}
Notice that I have an object called User, and it's used everywhere in this class.
I'd like to have classes called PostManager and AdminManager, which uses the same logic as the class above.
I could simply copy and paste the code above and replace the object User with Post and Admin. But...obviously this is bad practice.
What can I do to this class so that it accepts any resource? Not just User
The most obvious way to do something like this is to embed all of the generic functionality in a generic class, then inherit your UserManager from that:
protocol Managable {
init(json:JSON)
}
public class Manager<T:Manageable> {
let center = NSNotificationCenter.defaultCenter()
let queue = NSOperationQueue.mainQueue()
var resources = Dictionary<Int, T>()
var clients = Dictionary<Int, Set<String>>()
private init(){
}
private func addToClientMap(id: Int, clientName: String){
if clients[id] == nil {
clients[id] = Set<String>()
clients[id]!.insert(clientName)
}else{
clients[id]!.insert(clientName)
}
}
func getResource(id: Int, clientName: String) -> T?{
if let resource = resources[id] {
addToClientMap(id, clientName: clientName)
return resource
}else{
return nil
}
}
func createResource(data:JSON, clientName: String) -> T? {
if let id = data["id"].int {
if let resource = resources[id] {
addToClientMap(id, clientName: clientName)
return resource
}else{
resources[id] = T(json:data) //need to make generic
addToClientMap(id, clientName: clientName)
return resources[id]
}
}
return nil
}
func releaseResource(id: Int, clientName: String){
if clients[id] != nil {
clients[id]!.remove(clientName)
if clients[id]!.count == 0 {
resources.removeValueForKey(id)
clients.removeValueForKey(id)
}
}
}
}
class User : Managable {
required init(json:JSON) {
}
}
class UserManager : Manager<User> {
static var instance = UserManager()
}
Now then, any class that implements the Manageable protocol (ie., it has an init(json:JSON) method can have a Manager class variant. Note that since a generic class can't have a static property, that's been moved into the subclass.
Given that inheritance can hide implementation details, if reference semantics are not needed then a protocol + associated type (generics) implementation using structs might be safer and arguably more "Swifty".
Define your protocol with an associated type (Swift 2.2) or type alias (Swift 2.1):
protocol Manager {
associatedtype MyManagedObject // use typealias instead for Swift 2.1
func getResource(id: Int, clientName: String) -> MyManagedObject?
func createResource(data: JSON, clientName: String) -> MyManagedObject?
func releaseResource(id: Int, clientName: String)
}
And then your implementation becomes:
public struct UserManager: Manager {
typealias MyManagedObject = User
func getResource(id: Int, clientName: String) -> User? { ... }
func createResource(data: JSON, clientName: String) -> User? { ... }
func releaseResource(id: Int, clientName: String) { ... }
}
And you can further add objects using the same protocol easily, specifying what 'MyManagedObject' should be:
public struct PostManager: Manager {
typealias MyManagedObject = Post
func getResource(id: Int, clientName: String) -> Post? { ... }
func createResource(data: JSON, clientName: String) -> Post? { ... }
func releaseResource(id: Int, clientName: String) { ... }
}
I would recommend reading up more on protocols and generics in detail (there are many examples online, Apple's documentation is a good place to start).

Losing reference to a property of an instance in Swift

I'm encountering a problem where a property of an instance of a class I've created is seemingly losing reference to one its values.
Essentially I have a class like this:
class Channel {
var callbacks: [String: (JSON) -> Void]
var subscribed = false
let name: String
init(name: String) {
self.name = name
self.callbacks = [:]
}
func bind(eventName: String, callback: (JSON) -> Void) {
self.callbacks[eventName] = callback
}
func handleEvent(eventName: String, eventData: String) {
if let cb = self.callbacks[eventName] {
let json = JSON(object: eventData)
cb(json)
}
}
}
and then inside a ViewController I have the following code:
class ViewController: UIViewController {
let wSock = wSocket(key: "afa4d38348f89ba9c398")
func channelSetup() {
var ch = wSock.subscribe("test-channel")
ch.bind("test-event", { (data: JSON) -> Void in
println("I'm the callback getting called")
})
println(ch.callbacks)
}
override func viewDidLoad() {
super.viewDidLoad()
channelSetup()
}
}
In the println of ch.callbacks it shows that there is a key-value pair in the dictionary.
However, when the channel receives an event later on when there is a message received over the socket, the callback is no longer there. In terms of code, here is the code in full:
import UIKit
class ViewController: UIViewController {
let wSock = wSocketClient(key: "afa4d38348f89ba9c398")
func channelSetup() {
var ch = wSock.subscribe("test-channel")
ch.bind("test-event", { (data: JSON) -> Void in
println("I'm the callback getting called")
})
println(ch.callbacks)
}
override func viewDidLoad() {
super.viewDidLoad()
channelSetup()
}
}
class wSocketClient {
let connection: Connection
init(key: String, encrypted: Bool = false) {
var url = "SOCKET_URL"
connection = Connection(url: url)
}
func subscribe(channelName: String) -> Channel {
return self.connection.addChannel(channelName)
}
func connect() {
self.connection.open()
}
}
class Connection: WebSocketDelegate {
let url: String
lazy var socket: WebSocket = { [unowned self] in
return self.connectInternal()
}()
let connected = false
var channels = Channels()
init(url: String) {
self.url = url
}
func addChannel(channelName: String) -> Channel {
return Channel(name: channelName)
}
func open() {
if self.connected {
return
} else {
self.socket = connectInternal()
}
}
func connectInternal() -> WebSocket {
let ws = WebSocket(url: NSURL(string: self.url)!)
ws.delegate = self
ws.connect()
return ws
}
func websocketDidReceiveMessage(text: String) {
let data = (text as NSString).dataUsingEncoding(NSUTF8StringEncoding)
let json = JSON(data: data!)
if let channelName = json["channel"].stringValue {
if let internalChannel = self.channels.find(channelName) {
if let eName = json["event"].stringValue {
if let eData = json["data"].stringValue {
internalChannel.handleEvent(eName, eventData: eData) // this is the part of the code where the channel should eventually call the callback
}
}
}
}
}
}
class Channel {
var callbacks: [String: (JSON) -> Void]
var subscribed = false
let name: String
init(name: String) {
self.name = name
self.callbacks = [:]
}
func bind(eventName: String, callback: (JSON) -> Void) {
self.callbacks[eventName] = callback
}
func handleEvent(eventName: String, eventData: String) {
if let cb = self.callbacks[eventName] { // here self.callbacks is empty and the callback has disappeared
let json = JSON(object: eventData)
cb(json)
}
}
}
class Channels {
var channels = [String: Channel]()
func add(channelName: String) -> Channel {
if let channel = self.channels[channelName] {
return channel
} else {
let newChannel = Channel(name: channelName)
self.channels[channelName] = newChannel
return newChannel
}
}
func find(channelName: String) -> Channel? {
return self.channels[channelName]
}
}
So basically when the WebSocket receives some data that is for the given channel, it should check for the event name, and if there is a callback with that event name, call the callback associated to that event name. However, the channel apparently has no callbacks when the handleEvent method is called, even though at the bottom of viewDidLoad it shows as having a callback in the callbacks property for the channel.
Any ideas as to where / why the callback is disappearing?
Update
I've now tried moving the definition of the channel, ch outside of the channelSetup function so it's like this, but with no luck:
class ViewController: UIViewController {
let wSock = wSocket(key: "afa4d38348f89ba9c398")
var ch: Channel = nil
func channelSetup() {
ch = wSock.subscribe("test-channel")
ch.bind("test-event", { (data: JSON) -> Void in
println("I'm the callback getting called")
})
println(ch.callbacks)
}
override func viewDidLoad() {
super.viewDidLoad()
channelSetup()
}
}
I've solved this but not because it was something going on in Swift that I didn't understand. Instead it was just that the way that I had setup the code meant that there were duplicate channel objects being created and the callback was being added to only one of the channels.

Resources