LinkOS iOS Get IP from printer

// Expert user has replied.
L Lluis Menendez 11 months 2 weeks ago
132 10 0

We are developing a mobile application in which we need to obtain the IP address of the printer connected to the device via Bluetooth in order to perform certain printing-related operations. Currently, we have already developed the solution for Android, but we are experiencing issues with the iOS platform when attempting to obtain the printer's IP address.

We have tried using the ZebraPrinterFactory class provided by the Zebra SDK, specifically the getLinkOsPrinter method, but it resulted in a compilation error on iOS indicating that it is not available. We are using version 1.5.1049 of the SDK. Additionally, we have explored the possibility of using the NetworkDiscoverer class to discover the printer on the network, but we have not found a suitable method to directly obtain the printer's IP address since we do not know the specific range to search for the printer.

We would like to know the recommended way to obtain the IP address of the printer connected to the device. We would greatly appreciate it if you could provide us with information on how we can achieve this using the tools and functions available in the Zebra SDK.

 

Thanks!

Please register or login to post a reply

10 Replies

S Steven Si

What type of the printer models are you using? What development platform are you using for iOS, i.e., Xamarin or XCode?

For iOS, there is a dedicated library in the Link-OS SDK. To do a network discovery on iOS, you need to have the multicast entitlement from the Apple Developer Program. Without the entitlement, the iOS will block the multicast msg on iOS 14.5 and later.

L Lluis Menendez

Hi Steven, sorry for my delayed response.

I'm going to provide some more context to our situation. We are developing an app using Cordova on Xcode, and for printer connectivity, we have developed a plugin that utilizes the LinkOs library provided by Zebra. The printer model we are working with is the ZD410 and similar ones. As I mentioned in the initial post, on Android, we make use of the ZebraPrinterFactory class and its getLinkOsPrinter method. However, the iOS SDK does not provide these methods, and we are unable to retrieve the printer properties.

While reviewing the classes and methods available in the SDK, we discovered NetworkDiscoverer, and we were able to obtain the IP address of the printer connected on the network. However, to do this, we need to know the IP range it is within. For example, in my case, it is 192.168.5.X. But this is not reliable since a client, for example, could have an IP of 10.10.1.X.

That's why we wonder if there is a more efficient way to obtain the IP address of the printer that is already connected via Bluetooth to a new mobile device.

Thank you very much.

S Steven Si

Hi Lluis,

Thanks for providing the details. You are correct. The getLinkOsPrinter method was later added to the Link-OS SDK for Android, but it has never been added to the Link-OS SDK for iOS. That's the reason for the compilation error in XCode.

To obtain the IP address of the printer, we can use the SGD GET. Once the device is connected to the printer, we can call the SGD GET of interface.network.active.ip_addr to get the IP address alone of the printer, or the SGD GET of interface.network.active to get the complete information of the network connectivity.

// Assuming the Bluetooth connection is already established with the printer via the zebraPrinterConnection object
NSString *ipAddress = [SGD GET:"interface.network.active.ip_addr" withPrinterConnection:zebraPrinterConnection error:&error]; 

If the *ipAddress is empty, it means the printer is not network connected.

L Lluis Menendez

Hi Steven,

Thank you for your help. 

The code you provided is in Objective-C, but the plugin we are developing is in Swift. I have tried to adapt the suggested call, but I keep encountering errors. I'm attaching the code for the function where I make the printer status call and a small function where I make the call you suggested.

 /**
     * Get the status of the printer we are currently connected to
     *
     */
    @objc func printerStatus(_ command: CDVInvokedUrlCommand){
        DispatchQueue.global(qos: .background).async {
            NSLog("Getting ipAddress")
            let ipAddress = command.arguments[0] as? String ?? ""
            NSLog("IP PARAM: \(ipAddress)")

            var status = [String: Any]()
            status["connected"] = false
            status["isReadyToPrint"] = false
            status["isPaused"] = false
            status["isReceiveBufferFull"] = false
            status["isRibbonOut"] = false
            status["isPaperOut"] = false
            status["isHeadTooHot"] = false
            status["isHeadOpen"] = false
            status["isHeadCold"] = false
            status["isPartialFormatInProgress"] = false
            
            if(self.printerConnection != nil && self.printerConnection!.isConnected() && self.printer != nil){
                let zebraPrinterStatus = try? self.printer?.getCurrentStatus()
                if(zebraPrinterStatus != nil){
                    NSLog("Got Printer Status")
                    if(zebraPrinterStatus!.isReadyToPrint) { NSLog("Read To Print"); }
                    else {
                        let message = PrinterStatusMessages.init(printerStatus: zebraPrinterStatus)
                        if(message != nil)
                        {
                            NSLog("Printer Not Ready. " + (message!.getStatusMessage() as! [String]).joined(separator: ", "))
                        }else{
                            NSLog("Printer Not Ready.")
                        }
                    }
                    
                    status["connected"] = true
                    status["isReadyToPrint"] = zebraPrinterStatus?.isReadyToPrint
                    status["isPaused"] = zebraPrinterStatus?.isPaused
                    status["isReceiveBufferFull"] = zebraPrinterStatus?.isReceiveBufferFull
                    status["isRibbonOut"] = zebraPrinterStatus?.isRibbonOut
                    status["isPaperOut"] = zebraPrinterStatus?.isPaperOut
                    status["isHeadTooHot"] = zebraPrinterStatus?.isHeadTooHot
                    status["isHeadOpen"] = zebraPrinterStatus?.isHeadOpen
                    status["isHeadCold"] = zebraPrinterStatus?.isHeadCold
                    status["isPartialFormatInProgress"] = zebraPrinterStatus?.isPartialFormatInProgress
                    
                    self.getIPPrinter { ipAddress in
                        // Aquí puedes utilizar la dirección IP obtenida
                        if let ipAddress = ipAddress {
                            status["ip"] = ipAddress
                            NSLog("Printer's IP Address: \(ipAddress)")
                        } else {
                            NSLog("The printer's IP address could not be obtained.")
                        }
                    }                    
                    
                    NSLog("ZebraPrinter:: returning status")
                    let pluginResult = CDVPluginResult(
                        status: CDVCommandStatus_OK,
                        messageAs: status
                    )
                    self.commandDelegate!.send(
                        pluginResult,
                        callbackId: command.callbackId
                    )
                    return
                }else{
                    // printer has no status... this happens when the printer turns off, but the driver still thinks it is connected
                    NSLog("ZebraPrinter:: Got a printer but no status. Sadness.")
                    let pluginResult = CDVPluginResult(
                        status: CDVCommandStatus_ERROR,
                        messageAs: "Printer Has No Status"
                    )
                    self.commandDelegate!.send(
                        pluginResult,
                        callbackId: command.callbackId
                    )
                    return
                }
            }else{
                NSLog("ZebraPrinter:: status of disconnected printer")
                // if the printer isn't connected return success with disconnect status
                let pluginResult = CDVPluginResult(
                    status: CDVCommandStatus_OK,
                    messageAs: status
                )
                self.commandDelegate!.send(
                    pluginResult,
                    callbackId: command.callbackId
                )
                return
            }
        }
    }
func getIPPrinter(completion: @escaping (String?) -> Void) {
        DispatchQueue.global(qos: .background).async { 
            let ipAddressFound = SGD.GET("interface.network.active.ip_addr", withPrinterConnection: self.printerConnection)
            completion(ipAddressFound)
        }
    }

But when I try to compile, the following errors appear:
error: type of expression is ambiguous without more context
        DispatchQueue.global(qos: .background).async { 

Referring to the first line of the getIPPrinter function.


Can you help me? 

I'm not very knowledgeable in Swift or Objective-C, and we're adapting an existing plugin for the functionality we need.

Thank you very much.

S Steven Si

You should be able to call the Objective-C API from Swift through the Bridging Header. Please include the following header files in the Bridging Header

#import "ZebraPrinterConnection.h"
#import "ZebraPrinter.h"
#import "ZebraPrinterFactory.h"
#import "TcpPrinterConnection.h"
#import "MFiBtPrinterConnection.h"
#import "SGD.h"

 

L Lluis Menendez

Hi Steven,
Yes I include all these files on Bridging Header
 

#import "ResponseValidator.h"
#import "SGD.h"
#import "NetworkDiscoverer.h"
#import "DiscoveredPrinter.h"
#import "DiscoveredPrinterNetwork.h"
#import "ZebraPrinterFactory.h"
#import "ZebraPrinterConnection.h"
#import "ZebraPrinter.h"
#import "PrinterStatus.h"
#import "PrinterStatusMessages.h"
#import "TcpPrinterConnection.h"
#import "MFiBtPrinterConnection.h"

 

S Steven Si

Not sure if the ambiguous expression error is due to the unnecessary headers in the Bridging Header. Please limit the header files and place them in the order as shown in the following.

#import "MFiBtPrinterConnection.h"
#import "ZebraPrinterConnection.h"
#import "ZebraPrinter.h"
#import "ZebraPrinterFactory.h"
#import "TcpPrinterConnection.h"
#import "SGD.h"

 

L Lluis Menendez

Hello Steven,
I have been trying what you have been saying in your posts and I have not been able to make any progress.
I still have the problem of ambiguous expression error. 
Reviewing the SGD.h class I have seen that there are two GET methods. I leave below the file, but it is the same that Zebra indicates you to make use of the SDK.

 

/**********************************************
 * CONFIDENTIAL AND PROPRIETARY
 *
 * The information contained herein is the confidential and the exclusive property of
 * ZIH Corp. This document, and the information contained herein, shall not be copied, reproduced, published,
 * displayed or distributed, in whole or in part, in any medium, by any means, for any purpose without the express
 * written consent of ZIH Corp.
 *
 * Copyright ZIH Corp. 2012
 *
 * ALL RIGHTS RESERVED
 ***********************************************/

#import "ZebraPrinterConnection.h"

/**
 *  A utility class used to wrap and send SGD commands to a connection.
 * 
 *  Sets the IP Address of the connection.
 *  \code
#import "TcpPrinterConnection.h"
#import "ZebraPrinter.h"
#import "ZebraPrinterFactory.h"
#import "SGD.h"
#import <UIKit/UIKit.h>

TcpPrinterConnection *zebraPrinterConnection = [[TcpPrinterConnection alloc] initWithAddress:@"192.168.1.100" andWithPort:6101];
BOOL success = [zebraPrinterConnection open];
NSError *error = nil;
id<ZebraPrinter,NSObject> printer = [ZebraPrinterFactory getInstance:zebraPrinterConnection error:&error];
success = success && [SGD SET:@"ip.addr" withValue:@"192.168.1.100" andWithPrinterConnection:zebraPrinterConnection error:&error];

NSString *ipAddress = [SGD GET:@"ip.addr" withPrinterConnection:zebraPrinterConnection error:&error];
NSLog(@"The IP address is: %@", ipAddress);

[zebraPrinterConnection close];
if (error != nil || printer == nil || success == NO) {
	UIAlertView *errorAlert = [[UIAlertView alloc] initWithTitle:@"Error" message:[error localizedDescription] delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil];
	[errorAlert show];
	[errorAlert release];
}
[zebraPrinterConnection release];
 *  \endcode
 */
@interface SGD : NSObject {
}

/**
 * Constructs an SGD SET command and sends it to the printer. This method will not wait for a response from the
 * printer. If the SGD SET command returns a response, the caller is responsible for reading the response.
 * If a response is expected, use SGD::DO:withValue:andWithPrinterConnection:error: command.
 * 
 * @param setting The SGD setting.
 * @param value The setting's value.
 * @param printerConnection The connection to send the command to.
 * @param error Will be set to the error that occured.
 * @return <code>NO</code> if there was an error sending the SGD.
 */
+(BOOL) SET: (NSString*) setting 
	  withValue:(NSString*) value
andWithPrinterConnection: (id<ZebraPrinterConnection,NSObject>) printerConnection 
	  error:(NSError**)error;

/**
 * Constructs an SGD SET command and sends it to the printer. This method will not wait for a response from the
 * printer. If the SGD SET command returns a response, the caller is responsible for reading the response.
 * If a response is expected, use SGD::DO:withValue:andWithPrinterConnection:error: command.
 * 
 * @param setting The SGD setting.
 * @param value The setting's value.
 * @param printerConnection The connection to send the command to.
 * @param error Will be set to the error that occured.
 * @return <code>NO</code> if there was an error sending the SGD.
 */
+(BOOL) SET: (NSString*) setting 
	  withValueAsInt:(NSInteger) value
andWithPrinterConnection: (id<ZebraPrinterConnection,NSObject>) printerConnection 
	  error:(NSError**)error;

/**
 * Constructs an SGD GET command and sends it to the printer. This method waits for a maximum of
 * ZebraPrinterConnection::getMaxTimeoutForRead milliseconds for any data to be received. Once some data
 * has been received it waits until no more data is available within
 * ZebraPrinterConnection::getTimeToWaitForMoreData milliseconds. This method returns the SGD value
 * associated with <code>setting</code> without the surrounding quotes.
 * 
 * @param setting The SGD setting.
 * @param printerConnection The connection to send the command to.
 * @param error Will be set to the error that occured.
 * @return The setting's value or <code>nil</code> if an error occurred.
 */
+(NSString*) GET: (NSString*) setting 
withPrinterConnection: (id<ZebraPrinterConnection,NSObject>) printerConnection 
	   error:(NSError**)error;


/**
 * Constructs an SGD GET command and sends it to the printer. This method waits for a maximum of
 * maxTimeoutForRead milliseconds for any data to be received. Once some data
 * has been received it waits until no more data is available within
 * timeToWaitForMoreData milliseconds. This method returns the SGD value
 * associated with <code>setting</code> without the surrounding quotes.
 * 
 * @param setting The SGD setting.
 * @param printerConnection The connection to send the command to.
 * @param maxTimeoutForRead The maximum time, in milliseconds, to wait for a response from the printer.
 * @param timeToWaitForMoreData The maximum time, in milliseconds, to wait in between reads after the initial data
 * @param error Will be set to the error that occured.
 * @return The setting's value or <code>nil</code> if an error occurred.
 */
+(NSString*) GET: (NSString*) setting withPrinterConnection: (id<ZebraPrinterConnection,NSObject>) printerConnection withMaxTimeoutForRead:(NSInteger) 
		maxTimeoutForRead andWithTimeToWaitForMoreData:(NSInteger) timeToWaitForMoreData error:(NSError**)error;

/**
 * Constructs an SGD DO command and sends it to the printer. This method waits for a maximum of
 * ZebraPrinterConnection::getMaxTimeoutForRead milliseconds for any data to be received. Once some data
 * has been received it waits until no more data is available within
 * ZebraPrinterConnection::getTimeToWaitForMoreData milliseconds. This method returns
 * the SGD value associated with <code>setting</code> without the surrounding quotes.
 * 
 * @param setting The SGD setting.
 * @param value The setting's value.
 * @param printerConnection The connection to send the command to.
 * @param error Will be set to the error that occured.
 * @return The response from the SGD DO command.
 */
+(NSString*) DO: (NSString*) setting 
	  withValue:(NSString*) value
andWithPrinterConnection: (id<ZebraPrinterConnection,NSObject>) printerConnection 
	  error:(NSError**)error;

/**
 * Constructs an SGD DO command and sends it to the printer. This method waits for a maximum of
 * <code>maxTimeoutForRead</code> milliseconds for any data to be received. Once some data has been received it
 * waits until no more data is available within <code>timeToWaitForMoreData</code> milliseconds. This method returns
 * the SGD value associated with <code>setting</code> without the surrounding quotes.
 * 
 * @param setting The SGD setting.
 * @param value The setting's value.
 * @param printerConnection The connection to send the command to.
 * @param maxTimeoutForRead The maximum time, in milliseconds, to wait for a response from the printer.
 * @param timeToWaitForMoreData The maximum time, in milliseconds, to wait in between reads after the initial data
 * is received.
 * @param error Will be set to the error that occured.
 * @return The response from the SGD DO command.
 */
+(NSString*) DO: (NSString*) setting 
	  withValue:(NSString*) value 
withPrinterConnection: (id<ZebraPrinterConnection,NSObject>) printerConnection 
withMaxTimeoutForRead:(NSInteger) maxTimeoutForRead 
andWithTimeToWaitForMoreData:(NSInteger) timeToWaitForMoreData
	  error:(NSError**)error;


@end

 

If I try to comment one of the two GET methods I still get the same error, but if I comment both of them then it tells me that the GET method does not exist.
The truth is that I am very blocked and I don't know where to advance.

I comment you because we need the IP of the printer, to see if you can think of another way to be able to make the development that we want.

Our client wants to be able to print with the Zebra printer either by WIFI or Bluetooth. In the past with the application they are using right now they have problems that the printer is blocked when printing by bluetooth and that is why they ask us to make their new application to have the ability to print in both configurations. So to get the IP, when the printer is not yet configured to the network, is to send it a network connection configuration template. I wait for it to reboot and once it has rebooted and connected correctly to the network I try to get from its configuration that information. 
As I told you in Android the solution was with the ZebraPrinterFactory class.

See if with this you can tell me what next steps to try or follow.

Thank you very much

S Steven Si

Hi Lluis,

We will look into the ambiguous expression issue thrown out from Swift later. In the meantime, an alternative is to use the sendAndWaitForResponse API by sending the SGD command string, which is "! U1 getvar \"interface.network.active.ip_addr\"\n\r". It should return a string that contains the ip address of the printer.

L Lluis Menendez

Hi Steven,
I would try that. I will update if I get finally the IP :P
Thnks!

CONTACT
Can’t find what you’re looking for?