Cannot Log into Meteor on iOS with ObjectiveDDP - ios

I am trying out the objectiveDDP library to connect my iOS app to my Meteor app. I am running my meteor app on localhost:3000 and I am getting this error when I try to login:
Error Domain=boundsj.objectiveddp.transport Code=0 "You are not connected" UserInfo=0x7fe33b440500 {NSLocalizedDescription=You are not connected}
*Note - I have not done anything to the server to support this, I assuming that it is supported out of the box.
I'm not sure if the url I put in is correct because I couldn't find the documentation for which url I should connect to when using ObjectiveDDP.
Here is my code:
var meteorClient: MeteorClient = MeteorClient(DDPVersion: "1")
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
self.meteorClient.addSubscription("someSubscription")
var ddp = ObjectiveDDP(URLString: "ws://localhost:3000/websocket", delegate: self.meteorClient)
self.meteorClient.ddp = ddp;
self.meteorClient.ddp.connectWebSocket()
NSNotificationCenter.defaultCenter().addObserver(self, selector: "reportConnection", name: MeteorClientDidConnectNotification, object: nil)
}
func reportConnection() {
println("================> connected to server!")
println(self.meteorClient.connected)
self.meteorClient.logonWithEmail("blob#schmoe.com", password: "password", responseCallback: {
(response, error) -> Void in
if (error != nil) {
println("*****Error******")
println(error);
return;
}
println(response);
})
}
*Update
So I try to check whether my meteorClient is connected to the server before I login. I added
NSNotificationCenter.defaultCenter().addObserver(self, selector: "reportConnection", name: MeteorClientDidConnectNotification, object: nil)
To get notify when I am connected. However, when the DidConnectNotification is receive, I check self.meteorClient.connected and got false.

maintainer of said library, can you specify what versions of ObectiveDDP and meteor you are using? First try using ObectiveDDP from master not cocoapods. Your url is correct. First thing to check also, do the sample apps connect to your server?
Can you try your login by using a button instead, I think your hitting a race condition. This is something that could use a bit of tidy up.

Change your DDPVersion from 1 to pre2
var meteorClient: MeteorClient = MeteorClient(DDPVersion: "pre2")

I realize this is old, but thought I could help someone in the future. The issue seems to be that you used MeteorClientDidConnectNotification to decide when to start the login procedure. You should wait for the subsequent notification - MeteorClientConnectionReadyNotification. The button "fixed" it because you coincidentally waited after the MeteorClientDidConnectNotification for the client to become ready and for you to start the login procedure.

Related

Can't register for Sendbird push notifications in Swift

I have an iOS app in which I am using Sendbird successfully. Nevertheless, the functionality of receiving notifications when the app is running in background does not work. In fact, when I try to register the user for these notifications in the AppSelegate.swift file, I get the error 800101 ERR_CONNECTION_REQUIRED, what suggests I am not connecting correctly to the Sendbird server. However, I have checked that the connection is established without giving errors as soon as the app launches and before trying to register for those notifications.
This is the code for establishing the connection:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
SBDMain.initWithApplicationId(Constants.SENDBIRD_APP_ID, useCaching: false) {
} completionHandler: { error in
print(error)
}
...
}
This is my code for trying to register:
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Messaging.messaging().apnsToken = deviceToken
SBDMain.registerDevicePushToken(deviceToken, unique: true, completionHandler: { (status, error) in
if error == nil {
if status == SBDPushTokenRegistrationStatus.pending {
return
}
else {
print("Successfully registered")
}
}
else {
print(error)
}
})
}
As I said, I have made sure the sendbird application is initialized before the registration, but for some reason, when trying to register for the push notifications I get the "connection required" error. The rest of the Sendbird functionalities work fine, so I guess the connection is well established. The notifications work fine on Android using the same data.
Disclaimer: I am a Sendbird employee.
Hi Pepito,
I'm unsure specifically what version you're on however it looks like you may not be calling the actual connect method prior to trying to register the token.
There is a two step process when creating a connection. The first is initializing the SDK with your Application ID. The second is actually calling the connect method with the userId and accessToken(If you're using them).
// Initialize a SBDMain instance to use APIs in the client app.
SBDMain.initWithApplicationId(APP_ID, useCaching: false) {
} completionHandler: { error in
}
// The USER_ID below should be unique to your Sendbird application.
SBDMain.connect(withUserId: USER_ID, completionHandler: { (user, error) in
guard error == nil else {
// Handle error.
}
// The user is connected to Sendbird server.
...
})
If you're still having trouble after this, I suggest you reach out on our community forums so we can acquire more information from you. https://community.sendbird.com

Code working in AppDelegate but not in any other class

I am using socket.io client for swift. When I declare and connect the socket and use its commands in AppDelegate, things work fine. But as soon as I move this stuff from AppDelegate to another class (making socket a global variable) and call the function of that class from AppDelegate, socket errors start to appear (the description of errors is really irrelevant to this topic. I had a discussion regarding that in Socket.io issues)
The question I wanna ask is this happening? I have used
deinit {
NSLog("CCNOTIFICATIONS RESOURCES GOT DE INITIALIZED")
}
in that file but this never gets called.
According to my knowledge, AppDelegate is a delegate which gets called when there is some change in app's state i-e its launches or goes to background/foreground. And also that it is not a good practice to mess huge amount of code in AppDelegate.
Apparently there is no difference in scope of both files then why is this happening?
Please correct me if I am wrong in my understanding.
How this error problem can be solved? Or least what other approach I can try to get to root of this problem?
Any help would be appreciated.
UPDATE:
Here is the code:
When I write it in AppDelegate:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
public static var socket = SocketIOClient(socketURL: URL(string: “URL”)!, config: [.log(true), .compress])
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
AppDelegate.socket.connect()
AppDelegate.socket.on("connect") {data, ack in
print("Connected successfully")
}
AppDelegate.socket.on("book-adventure-provider") {data, ack in
print("BOOKING CAME")
print(data)
}
return true
}
When I write it in other file:
CCNotifications:
public class CCNotifications : INotifications
{
public var socket = SocketIOClient(socketURL: URL(string: “URL”)!, config: [.log(true), .compress, .forcePolling(true), .forceNew(true)])
public func sendBookingRequest(adventureId: String, success: #escaping () -> Void, error: #escaping CErrorCallback) {
if (ConnectionStatus.isConnected() == false)
{
let errorObj = CError()
errorObj.message = "No internet connection"
errorObj.code = ClientConstants.connectionErrorCode
error(errorObj)
}
else
{
let jwtToken = KeychainWrapper.standard.string(forKey: "jwtToken")
let data = ["adventure_id": adventureId, "jwt": jwtToken]
socket.emit("book-adventure", data)
socket.on("book-adventure-seeker") { data, ack in
print(data)
}
}
}
}
CuufyClient:
public class CuufyClient : IClient {
public var notifications: INotifications? = nil
public static var Notifications: INotifications? { get { return CuufyClient.client?.notifications}}
//region Actions
public static func Initialize(completion: ((_ error: CError?) -> Void)?) {
CuufyClient.client?.notifications = CCNotifications ()
completion? (nil)
}
//endregion
}
Finally calling it as:
CuufyClient.Notifications?.funcWhichIWannaCall.
Note:
Both ways work. But when I write code in CCNotifications and run it, at sometimes it starts giving socket error: Session id unknown and then connects itself again and again.
UPDATE 2:
A line before the socket error in the logs I have observed this error:
2017-12-15 15:45:34.133480+0500 TestTarget[5332:230404] TIC Read Status [1:0x60000017f440]: 1:57
Upon searching this error I got to know that in Xcode 9, this error shows that TCP connection has closed. When I opened this issue on socket.io branch the specialist said:
This library operates on a fairly high level above the underlying
networking. It relies on either URLSession or Starscream to tell it
when the connection is lost.
Can anyone help me regarding URLSession that why TCP is getting closed as I am fairly new to iOS.
First to say, your approach of keeping the AppDelegate thin is a good idea.
Regarding the deinit: This function will be called when the object ceases to exist. Since global variables will life "forever" - as long as they are assigned to a different object - they will be cleaned up only at program termination (not: background mode etc.) Therefore, you will never see the deinit call.
Your thoughts about the life cycle are correct, there shouldn't be a difference if you have done everything correctly - which I cannot judge here because you didn't show any code of how and when that global socket is created/initialized etc.
Regarding the updated question: Unfortunatly, I don't know the socket.io library. But having just a short look, your code differs slightly between the AppDelegate and the other version. It might be a timeing issue - just check (and maybe fix) a few things:
When do you call connect()?
You might want to first set up all the ...on()-Handlers before connecting or calling emit; otherwise the server might respond before everything has set up correctly
Check if you might set up the ...on()-Handlers only one, centrally, and not with every sendBookingRequest-call
What is jwtToken? Is it an API token for you application, or some kind of seesion token? When do you store the value, and why is it stored in the keychain? Might its value change during requests/responses?
You should not place socket event listener in sendBookingRequest method.
You just need to bind a socket event listener once.
socket.on("book-adventure-seeker") { data, ack in
print(data)
}
so, you can place it in on connect event.
AppDelegate.socket.on("connect") {data, ack in
print("Connected successfully")
}

Send push notification to a specific test device using OneSignal push notification service

I added push notification support to my iOS app using OneSignal some time before. The app is made in Xcode with Swift.
I want to send a test push notification only to my test device(s). I the documentation I found the following manual: How do I send a notification to a single user?
I managed to create the segment but I don't know where to put this peace of code: OneSignal.sendTag("is_test", "true")
Does anybody know where I have to put this piece of code to make it working as I described above?
I uploaded my code here: https://codeshare.io/DxcNn
Thanks,
David.
Update:
OneSignal now also supports to set a device as test device without doing something in the code. You can also download your own app from App Store and use it as test device. Just select you device from devices list one OneSignal and mark it as test device. You can find your device in the list by model, version and/or time added.
The sendTag method is from the device sdk. In your case iOS.
https://documentation.onesignal.com/docs/ios-native-sdk#section--sendtag-
You should do this anytime after initWithLaunchOptions in the app delegate. Updated code based on comments
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {
let oneSignal = OneSignal(launchOptions: launchOptions, appId: "here_is_my_onesignal_app_id") { (message, additionalData, isActive) in
NSLog("OneSignal Notification opened:\nMessage: %#", message)
if additionalData != nil {
NSLog("additionalData: %#", additionalData)
// Check for and read any custom values you added to the notification
// This done with the "Additonal Data" section the dashbaord.
// OR setting the 'data' field on our REST API.
if let customKey = additionalData["customKey"] as! String? {
NSLog("customKey: %#", customKey)
}
}
}
OneSignal.defaultClient().sendTag("is_test", value: "true")
// Override point for customization after application launch.
return true
}

Reachability not worked when user launch app offline

I used Reachability Class from apple to check internet connection. I'll share to codes in the below. It's working fine if i open the app with online. But if i try to open app offline Reachability function is not triggered. Can anyone help me to solve this issue.
Thank you
Here is the code which are locate in AppDelegate.swift
var reachability: Reachability?
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
NSNotificationCenter.defaultCenter().addObserver(self, selector: "checkForReachability:", name:kReachabilityChangedNotification, object: nil)
reachability = Reachability.reachabilityForInternetConnection();
reachability?.startNotifier();
return true
}
func checkForReachability(notification: NSNotification) {
let title_alert = NSLocalizedString("Connection Problem", comment: "Connection Problem")
let remoteHostStatus = self.reachability!.currentReachabilityStatus()
if (remoteHostStatus == NotReachable) {
let myAlert = UIAlertController(title:title_alert, message:NSLocalizedString("Please Check Your Connection", comment: "Connection Problem"), preferredStyle: UIAlertControllerStyle.Alert)
let okAction = UIAlertAction(title:NSLocalizedString("Try Again",comment:"Connection Problem"), style:UIAlertActionStyle.Default) {
action in
self.checkForReachability(notification)
}
myAlert.addAction(okAction)
self.window?.rootViewController?.presentViewController(myAlert, animated:true, completion:nil)
}
}
Reachability send you notifications when the state changes. If you have no internet, and this doesn't change, no notification will ever be sent. You need to check once by hand, probably after creating the reachability object. To check, you just check currentReachabilityStatus() outside the notification callback.
PS. I wouldn't send an alert when the internet goes away, but when the user actually does something that would require the internet and it doesn't work. Things like background updates shouldn't be visible to the user and shouldn't show an alert. (Bad example is iTunes on a Mac, which tells you all the time that it couldn't connect to the iTunes store, when I had no intention whatsoever to connect to it).
What exactly you do obviously depends on your app. Take Safari - it doesn't tell you there is no internet connection, only when you connect to a page. There are plenty of features that are useful without internet access.
If your app is absolutely useless without internet access and there is nothing better you can do, display a page that says "no internet". If there are things the user can do, let them do those things. Programmers often see these things from a programmer's point of view: Something went wrong, and everyone told you that you should inform the user. From the user's perspective: Being told with repeated alerts will get on their nerves. Imagine you are a user who just turned on AirPlane mode. That user thinks "for heaven's sake, I know there is no WiFi because I just turned it off. Don't keep telling me".
This is a hard one to answer with all of the code that Reachability requires, but here is the video I used when I set up Reachability for my app! It uses NSNotificationCenter to constantly check if the user is online. When the user goes offline, you can have an action occur until the user comes back online.
create a UIViewController extension and add a function like this -
func networkChanged() {
if let isReachable = Reachability()?.isReachable, isReachable == true {
noInternetErrorManager.removeNoInternetView()
} else {
noInternetErrorManager.displayNoInternetError(forParentView: view)
}
}
Now call this function on viewDidLoad()

How do I exactly use MMWormhole with Swift?

I have an iPhone application and added a WatchKitExtension. From the iPhone App I want to pass a String to the WatchApp which is supposed to change an image on the Watch.
What I already did was to download the source files and import the MMWormhole.m & .h. They are written in Obj-C and so Xcode automatically bridged them for me.
I also added an app group and activated it for my WatchExtension & my iPhone target
In the tutorial on GitHub it says I have to initialize the wormhole with:
self.wormhole = [[MMWormhole alloc] initWithApplicationGroupIdentifier:#"group.com.mutualmobile.wormhole"
optionalDirectory:#"wormhole"];
...and send a message using:
[self.wormhole passMessageObject:#{#"titleString" : title}
identifier:#"messageIdentifier"];
But I have actually no idea where to put that, I am using Swift in my iPhone application and the WatchExtension.
Can anyone please help me there?
I suppose it depends on different applications, but for one application I put the listeners in the didFinishLaunchingWithOptions method of my app delegate in the main iOS app. This was because the user would be using the watch, and they would be relaying information off to the phone
There were multiple listeners...
var wormhole = MMWormhole(applicationGroupIdentifier: "group", optionalDirectory: nil)
wormhole.listenForMessageWithIdentifier("identifier", listener: { (message) -> Void in
//do stuff
})
wormhole.listenForMessageWithIdentifier("identifier2", listener: { (message) -> Void in
//do stuff
})
wormhole.listenForMessageWithIdentifier("identifier3", listener: { (message) -> Void in
//do stuff
})
And then in a WKInterfaceController, I sent a message. Sometimes in an action, sometimes in the willActivate method. It really depends on the flow of your app
var wormhole = MMWormhole(applicationGroupIdentifier: "group", optionalDirectory: nil)
#IBAction func buttonPushed(){
wormhole.passMessageObject("object", identifier: "identifier1")
}
This can work both ways though, I could have very easily put a listener in my watch which would wait for messages initiated by some Interface Controller on the phone.
Here are my instructions. Hopefully they help you with the simplest use case and then you can expand from there. (Remember to structure your code so that it actually makes sense!)
Get MMWormhole (the .h and the .m) added to your project. If you know how to use Cocoapods, do that, but otherwise, just use git submodules. (I use git submmodules)
Because you need the .h to be visible from Swift, you need to use a bridging header.
Set up an App Group, which requires using the Developer Portal. Link is here
In your iPhone build target -> Capabilities -> App Groups and add your group. If all three checkboxes do not go perfectly, go back to the Developer Portal and make sure everything is right or start again.
MMWormhole, iPhone Side
Set up the wormhole somewhere you can reach it. NOTE: your group ID has to be the one from above!
let wormhole = MMWormhole(applicationGroupIdentifier: "group.testMe.now", optionalDirectory: nil)
wormhole.listenForMessageWithIdentifier("wormholeMessageFromWatch", listener: { (message ) -> Void in
if let messageFromWatch = message as? String {
// do something with messageFromWatch
}
})
iPhone App Sends String
wormhole.passMessageObject("message from phone to watch", identifier: "wormholeMessageFromPhone")
iPhone app registers to receive and sends again in the callback via MMWormhole (asynchronous but cool)
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
universe.initWormhole(.phone, messageHandler: { (message) -> () in
universe.wormhole.passMessageObject("the phone got \(message)", identifier: "wormholeMessageFromPhone")
})
return true
}
MMWormhole, Apple Watch Side
Set up the wormhole somewhere you can reach it. NOTE: your group ID has to be the one from above!
let wormhole = MMWormhole(applicationGroupIdentifier: "group.testMe.now", optionalDirectory: nil)
wormhole.listenForMessageWithIdentifier("wormholeMessageFromPhone", listener: { (message ) -> Void in
if let messageFromPhone = message as? String {
// do something with messageFromPhone
}
})
MMWormhole, watch app registers to receive
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
universe.initWormhole(.watch, messageHandler: { (message) -> () in
println("MMWormhole Message Came to Watch: \(message)")
})
}
MMWormhole, watch app sends
// force open the parent application because otherwise the message goes nowhere until the app is opened
WKInterfaceController.openParentApplication(["":""], reply: nil)
universe.wormhole.passMessageObject("[from watch to phone]", identifier: "wormholeMessageFromWatch")

Resources