I've searched over this topic but found very few details which were helpful. With these details I've tried to cook some code as follows.
Note: Please compare the details shared in this post with other posts before marking this as DUPLICATE, and not just by the subject.
- (NSArray *)getDataCountersForType:(int)type {
BOOL success;
struct ifaddrs *addrs = nil;
const struct ifaddrs *cursor = nil;
const struct sockaddr_dl *dlAddr = nil;
const struct if_data *networkStatisc = nil;
int dataSent = 0;
int dataReceived = 0;
success = getifaddrs(&addrs) == 0;
if (success) {
cursor = addrs;
while (cursor != NULL) {
if (cursor->ifa_addr->sa_family == AF_LINK) {
dlAddr = (const struct sockaddr_dl *) cursor->ifa_addr;
networkStatisc = (const struct if_data *) cursor->ifa_data;
if (type == WiFi) {
dataSent += networkStatisc->ifi_opackets;
dataReceived += networkStatisc->ifi_ipackets;
}
else if (type == WWAN) {
dataSent += networkStatisc->ifi_obytes;
dataReceived += networkStatisc->ifi_ibytes;
}
}
cursor = cursor->ifa_next;
}
freeifaddrs(addrs);
}
return [NSArray arrayWithObjects:[NSNumber numberWithInt:dataSent], [NSNumber numberWithInt:dataReceived], nil];
}
This code collects information of internet usage of an iPhone device (and not my application alone).
Now, if I use internet through WiFi or through 3G, I get the the data (bytes) only in ifi_obytes (sent) and ifi_ibytes (received) but I think I should get WiFi usage in ifi_opackets and ifi_ipackets.
Also wanted to add that if I'm connected to a WiFi network, but am not using internet, I still get value added to ifi_obytes and ifi_ibytes.
May be I'm wrong in the implementation or understanding. Need someone to help me out.
Edit: Instead of AF_LINK I tried AF_INET (sockaddr_in instead of sockaddr_dl). This crashes the application.
The thing is that pdp_ip0 is one of interfaces, all pdpXXX are WWAN interfaces dedicated to different functions, voicemail, general networking interface.
I read in Apple forum that :
The OS does not keep network statistics on a process-by-process basis. As such, there's no exact solution to this problem. You can, however, get network statistics for each network interface.
In general en0 is your Wi-Fi interface and pdp_ip0 is your WWAN interface.
There is no good way to get information wifi/cellular network data since, particular date-time!
Data statistic (ifa_data->ifi_obytes and ifa_data->ifi_ibytes) are stored from previous device reboot.
I don't know why, but ifi_opackets and ifi_ipackets are shown just for lo0 (I think its main interface ).
Yes. Then device is connected via WiFi and doesn't use internet if_iobytes values still come because this method provides network bytes exchanges and not just internet.
#include <net/if.h>
#include <ifaddrs.h>
static NSString *const DataCounterKeyWWANSent = #"WWANSent";
static NSString *const DataCounterKeyWWANReceived = #"WWANReceived";
static NSString *const DataCounterKeyWiFiSent = #"WiFiSent";
static NSString *const DataCounterKeyWiFiReceived = #"WiFiReceived";
NSDictionary *DataCounters()
{
struct ifaddrs *addrs;
const struct ifaddrs *cursor;
u_int32_t WiFiSent = 0;
u_int32_t WiFiReceived = 0;
u_int32_t WWANSent = 0;
u_int32_t WWANReceived = 0;
if (getifaddrs(&addrs) == 0)
{
cursor = addrs;
while (cursor != NULL)
{
if (cursor->ifa_addr->sa_family == AF_LINK)
{
#ifdef DEBUG
const struct if_data *ifa_data = (struct if_data *)cursor->ifa_data;
if (ifa_data != NULL)
{
NSLog(#"Interface name %s: sent %tu received %tu",cursor->ifa_name,ifa_data->ifi_obytes,ifa_data->ifi_ibytes);
}
#endif
// name of interfaces:
// en0 is WiFi
// pdp_ip0 is WWAN
NSString *name = #(cursor->ifa_name);
if ([name hasPrefix:#"en"])
{
const struct if_data *ifa_data = (struct if_data *)cursor->ifa_data;
if (ifa_data != NULL)
{
WiFiSent += ifa_data->ifi_obytes;
WiFiReceived += ifa_data->ifi_ibytes;
}
}
if ([name hasPrefix:#"pdp_ip"])
{
const struct if_data *ifa_data = (struct if_data *)cursor->ifa_data;
if (ifa_data != NULL)
{
WWANSent += ifa_data->ifi_obytes;
WWANReceived += ifa_data->ifi_ibytes;
}
}
}
cursor = cursor->ifa_next;
}
freeifaddrs(addrs);
}
return #{DataCounterKeyWiFiSent : #(WiFiSent),
DataCounterKeyWiFiReceived : #(WiFiReceived),
DataCounterKeyWWANSent : #(WWANSent),
DataCounterKeyWWANReceived : #(WWANReceived)};
}
Improved copy/paste support !
It's important to understand that these counters are provided since the device's last boot.
So, to make effective use of them, you should accompany every sample with the device's uptime (you can use mach_absolute_time() - see this for more information)
Once you have counters samples + uptime you can have better heuristics as to data use...
To add to the accepted answer, its important to realize that the amount of data displayed by the interface overflows and restarts at 0 after every 4 GB, especially if you are using this code to calculate the difference between two readings. This is because ifi_obytes and ifi_ibytes are uint_32 and their max value is 4294967295.
Also, I recommend using unsigned ints for the variables containing the data sent and received. Regular ints have half the max value of an unsigned integer, so when adding ifi_obytes, it may cause an overflow.
unsigned int sent = 0;
sent += networkStatisc->ifi_obytes;
Swift version of the accepted answer. I also break the code into smaller units.
struct DataUsageInfo {
var wifiReceived: UInt32 = 0
var wifiSent: UInt32 = 0
var wirelessWanDataReceived: UInt32 = 0
var wirelessWanDataSent: UInt32 = 0
mutating func updateInfoByAdding(info: DataUsageInfo) {
wifiSent += info.wifiSent
wifiReceived += info.wifiReceived
wirelessWanDataSent += info.wirelessWanDataSent
wirelessWanDataReceived += info.wirelessWanDataReceived
}
}
class DataUsage {
private static let wwanInterfacePrefix = "pdp_ip"
private static let wifiInterfacePrefix = "en"
class func getDataUsage() -> DataUsageInfo {
var interfaceAddresses: UnsafeMutablePointer<ifaddrs> = nil
var dataUsageInfo = DataUsageInfo()
guard getifaddrs(&interfaceAddresses) == 0 else { return dataUsageInfo }
var pointer = interfaceAddresses
while pointer != nil {
guard let info = getDataUsageInfo(from: pointer) else {
pointer = pointer.memory.ifa_next
continue
}
dataUsageInfo.updateInfoByAdding(info)
pointer = pointer.memory.ifa_next
}
freeifaddrs(interfaceAddresses)
return dataUsageInfo
}
private class func getDataUsageInfo(from infoPointer: UnsafeMutablePointer<ifaddrs>) -> DataUsageInfo? {
let pointer = infoPointer
let name: String! = String.fromCString(infoPointer.memory.ifa_name)
let addr = pointer.memory.ifa_addr.memory
guard addr.sa_family == UInt8(AF_LINK) else { return nil }
return dataUsageInfo(from: pointer, name: name)
}
private class func dataUsageInfo(from pointer: UnsafeMutablePointer<ifaddrs>, name: String) -> DataUsageInfo {
var networkData: UnsafeMutablePointer<if_data> = nil
var dataUsageInfo = DataUsageInfo()
if name.hasPrefix(wifiInterfacePrefix) {
networkData = unsafeBitCast(pointer.memory.ifa_data, UnsafeMutablePointer<if_data>.self)
dataUsageInfo.wifiSent += networkData.memory.ifi_obytes
dataUsageInfo.wifiReceived += networkData.memory.ifi_ibytes
} else if name.hasPrefix(wwanInterfacePrefix) {
networkData = unsafeBitCast(pointer.memory.ifa_data, UnsafeMutablePointer<if_data>.self)
dataUsageInfo.wirelessWanDataSent += networkData.memory.ifi_obytes
dataUsageInfo.wirelessWanDataReceived += networkData.memory.ifi_ibytes
}
return dataUsageInfo
}
}
I fixed above source code to Swift3 version
struct DataUsageInfo {
var wifiReceived: UInt32 = 0
var wifiSent: UInt32 = 0
var wirelessWanDataReceived: UInt32 = 0
var wirelessWanDataSent: UInt32 = 0
mutating func updateInfoByAdding(_ info: DataUsageInfo) {
wifiSent += info.wifiSent
wifiReceived += info.wifiReceived
wirelessWanDataSent += info.wirelessWanDataSent
wirelessWanDataReceived += info.wirelessWanDataReceived
}
}
class DataUsage {
private static let wwanInterfacePrefix = "pdp_ip"
private static let wifiInterfacePrefix = "en"
class func getDataUsage() -> DataUsageInfo {
var ifaddr: UnsafeMutablePointer<ifaddrs>?
var dataUsageInfo = DataUsageInfo()
guard getifaddrs(&ifaddr) == 0 else { return dataUsageInfo }
while let addr = ifaddr {
guard let info = getDataUsageInfo(from: addr) else {
ifaddr = addr.pointee.ifa_next
continue
}
dataUsageInfo.updateInfoByAdding(info)
ifaddr = addr.pointee.ifa_next
}
freeifaddrs(ifaddr)
return dataUsageInfo
}
private class func getDataUsageInfo(from infoPointer: UnsafeMutablePointer<ifaddrs>) -> DataUsageInfo? {
let pointer = infoPointer
let name: String! = String(cString: pointer.pointee.ifa_name)
let addr = pointer.pointee.ifa_addr.pointee
guard addr.sa_family == UInt8(AF_LINK) else { return nil }
return dataUsageInfo(from: pointer, name: name)
}
private class func dataUsageInfo(from pointer: UnsafeMutablePointer<ifaddrs>, name: String) -> DataUsageInfo {
var networkData: UnsafeMutablePointer<if_data>?
var dataUsageInfo = DataUsageInfo()
if name.hasPrefix(wifiInterfacePrefix) {
networkData = unsafeBitCast(pointer.pointee.ifa_data, to: UnsafeMutablePointer<if_data>.self)
if let data = networkData {
dataUsageInfo.wifiSent += data.pointee.ifi_obytes
dataUsageInfo.wifiReceived += data.pointee.ifi_ibytes
}
} else if name.hasPrefix(wwanInterfacePrefix) {
networkData = unsafeBitCast(pointer.pointee.ifa_data, to: UnsafeMutablePointer<if_data>.self)
if let data = networkData {
dataUsageInfo.wirelessWanDataSent += data.pointee.ifi_obytes
dataUsageInfo.wirelessWanDataReceived += data.pointee.ifi_ibytes
}
}
return dataUsageInfo
}
}
A new version about based on previous versions, but adapted for Swift4 and Xcode 9
struct DataUsageInfo {
var wifiReceived: UInt32 = 0
var wifiSent: UInt32 = 0
var wirelessWanDataReceived: UInt32 = 0
var wirelessWanDataSent: UInt32 = 0
mutating func updateInfoByAdding(info: DataUsageInfo) {
wifiSent += info.wifiSent
wifiReceived += info.wifiReceived
wirelessWanDataSent += info.wirelessWanDataSent
wirelessWanDataReceived += info.wirelessWanDataReceived
}
}
class DataUsage {
private static let wwanInterfacePrefix = "pdp_ip"
private static let wifiInterfacePrefix = "en"
class func getDataUsage() -> DataUsageInfo {
var interfaceAddresses: UnsafeMutablePointer<ifaddrs>? = nil
var dataUsageInfo = DataUsageInfo()
guard getifaddrs(&interfaceAddresses) == 0 else { return dataUsageInfo }
var pointer = interfaceAddresses
while pointer != nil {
guard let info = getDataUsageInfo(from: pointer!) else {
pointer = pointer!.pointee.ifa_next
continue
}
dataUsageInfo.updateInfoByAdding(info: info)
pointer = pointer!.pointee.ifa_next
}
freeifaddrs(interfaceAddresses)
return dataUsageInfo
}
private class func getDataUsageInfo(from infoPointer: UnsafeMutablePointer<ifaddrs>) -> DataUsageInfo? {
let pointer = infoPointer
let name: String! = String(cString: infoPointer.pointee.ifa_name)
let addr = pointer.pointee.ifa_addr.pointee
guard addr.sa_family == UInt8(AF_LINK) else { return nil }
return dataUsageInfo(from: pointer, name: name)
}
private class func dataUsageInfo(from pointer: UnsafeMutablePointer<ifaddrs>, name: String) -> DataUsageInfo {
var networkData: UnsafeMutablePointer<if_data>? = nil
var dataUsageInfo = DataUsageInfo()
if name.hasPrefix(wifiInterfacePrefix) {
networkData = unsafeBitCast(pointer.pointee.ifa_data, to: UnsafeMutablePointer<if_data>.self)
dataUsageInfo.wifiSent += networkData?.pointee.ifi_obytes ?? 0
dataUsageInfo.wifiReceived += networkData?.pointee.ifi_ibytes ?? 0
} else if name.hasPrefix(wwanInterfacePrefix) {
networkData = unsafeBitCast(pointer.pointee.ifa_data, to: UnsafeMutablePointer<if_data>.self)
dataUsageInfo.wirelessWanDataSent += networkData?.pointee.ifi_obytes ?? 0
dataUsageInfo.wirelessWanDataReceived += networkData?.pointee.ifi_ibytes ?? 0
}
return dataUsageInfo
}
}
Sorry for same answer again.
but I found that UInt32 is not enough, so it crashes when it became too big.
I just changed UInt32 to UInt64 and it works fine.
struct DataUsageInfo {
var wifiReceived: UInt64 = 0
var wifiSent: UInt64 = 0
var wirelessWanDataReceived: UInt64 = 0
var wirelessWanDataSent: UInt64 = 0
mutating func updateInfoByAdding(info: DataUsageInfo) {
wifiSent += info.wifiSent
wifiReceived += info.wifiReceived
wirelessWanDataSent += info.wirelessWanDataSent
wirelessWanDataReceived += info.wirelessWanDataReceived
}
}
class DataUsage {
private static let wwanInterfacePrefix = "pdp_ip"
private static let wifiInterfacePrefix = "en"
class func getDataUsage() -> DataUsageInfo {
var interfaceAddresses: UnsafeMutablePointer<ifaddrs>? = nil
var dataUsageInfo = DataUsageInfo()
guard getifaddrs(&interfaceAddresses) == 0 else { return dataUsageInfo }
var pointer = interfaceAddresses
while pointer != nil {
guard let info = getDataUsageInfo(from: pointer!) else {
pointer = pointer!.pointee.ifa_next
continue
}
dataUsageInfo.updateInfoByAdding(info: info)
pointer = pointer!.pointee.ifa_next
}
freeifaddrs(interfaceAddresses)
return dataUsageInfo
}
private class func getDataUsageInfo(from infoPointer: UnsafeMutablePointer<ifaddrs>) -> DataUsageInfo? {
let pointer = infoPointer
let name: String! = String(cString: infoPointer.pointee.ifa_name)
let addr = pointer.pointee.ifa_addr.pointee
guard addr.sa_family == UInt8(AF_LINK) else { return nil }
return dataUsageInfo(from: pointer, name: name)
}
private class func dataUsageInfo(from pointer: UnsafeMutablePointer<ifaddrs>, name: String) -> DataUsageInfo {
var networkData: UnsafeMutablePointer<if_data>? = nil
var dataUsageInfo = DataUsageInfo()
if name.hasPrefix(wifiInterfacePrefix) {
networkData = unsafeBitCast(pointer.pointee.ifa_data, to: UnsafeMutablePointer<if_data>.self)
dataUsageInfo.wifiSent += UInt64(networkData?.pointee.ifi_obytes ?? 0)
dataUsageInfo.wifiReceived += UInt64(networkData?.pointee.ifi_ibytes ?? 0)
} else if name.hasPrefix(wwanInterfacePrefix) {
networkData = unsafeBitCast(pointer.pointee.ifa_data, to: UnsafeMutablePointer<if_data>.self)
dataUsageInfo.wirelessWanDataSent += UInt64(networkData?.pointee.ifi_obytes ?? 0)
dataUsageInfo.wirelessWanDataReceived += UInt64(networkData?.pointee.ifi_ibytes ?? 0)
}
return dataUsageInfo
}
}
Related
I have the following code to decompress some Data back to a String in Swift 5. The method mostly works fine, but sometimes it fails with the following error message:
Thread 1: Fatal error: UnsafeMutablePointer.initialize overlapping range
extension Data
{
func decompress(destinationSize: Int) -> String?
{
let destinationBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: destinationSize)
let decodedString = self.withUnsafeBytes
{
unsafeRawBufferPointer -> String? in
let unsafeBufferPointer = unsafeRawBufferPointer.bindMemory(to: UInt8.self)
if let unsafePointer = unsafeBufferPointer.baseAddress
{
let decompressedSize = compression_decode_buffer(destinationBuffer, destinationSize, unsafePointer, self.count, nil, COMPRESSION_ZLIB)
if decompressedSize == 0
{
return String.empty
}
let string = String(cString: destinationBuffer)
let substring = string.substring(0, decompressedSize)
return substring
}
return nil
}
return decodedString
}
}
The error occurs at the following line:
let string = String(cString: destinationBuffer)
Can someone please explain why this (sometimes) fails?
I have switched to the following code and now everything works fine (Swift 5):
import Compression
extension Data
{
func compress() -> Data?
{
return self.withUnsafeBytes
{
dataBytes in
let sourcePtr: UnsafePointer<UInt8> = dataBytes.baseAddress!.assumingMemoryBound(to: UInt8.self)
return self.perform(operation: COMPRESSION_STREAM_ENCODE, source: sourcePtr, sourceSize: self.count)
}
}
func decompress() -> Data?
{
return self.withUnsafeBytes
{
unsafeRawBufferPointer -> Data? in
let unsafeBufferPointer = unsafeRawBufferPointer.bindMemory(to: UInt8.self)
if let unsafePointer = unsafeBufferPointer.baseAddress
{
return self.perform(operation: COMPRESSION_STREAM_DECODE, source: unsafePointer, sourceSize: self.count)
}
return nil
}
}
fileprivate func perform(operation: compression_stream_operation, source: UnsafePointer<UInt8>, sourceSize: Int, preload: Data = Data()) -> Data?
{
guard sourceSize > 0 else { return nil }
let streamBase = UnsafeMutablePointer<compression_stream>.allocate(capacity: 1)
defer { streamBase.deallocate() }
var stream = streamBase.pointee
let status = compression_stream_init(&stream, operation, COMPRESSION_ZLIB)
guard status != COMPRESSION_STATUS_ERROR else { return nil }
defer { compression_stream_destroy(&stream) }
var result = preload
var flags: Int32 = Int32(COMPRESSION_STREAM_FINALIZE.rawValue)
let blockLimit = 64 * 1024
var bufferSize = Swift.max(sourceSize, 64)
if sourceSize > blockLimit
{
bufferSize = blockLimit
}
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: bufferSize)
defer { buffer.deallocate() }
stream.dst_ptr = buffer
stream.dst_size = bufferSize
stream.src_ptr = source
stream.src_size = sourceSize
while true
{
switch compression_stream_process(&stream, flags)
{
case COMPRESSION_STATUS_OK:
guard stream.dst_size == 0 else { return nil }
result.append(buffer, count: stream.dst_ptr - buffer)
stream.dst_ptr = buffer
stream.dst_size = bufferSize
if flags == 0 && stream.src_size == 0
{
flags = Int32(COMPRESSION_STREAM_FINALIZE.rawValue)
}
case COMPRESSION_STATUS_END:
result.append(buffer, count: stream.dst_ptr - buffer)
return result
default:
return nil
}
}
}
}
How do I make a custom completion handler for the below function? This is writing to a websocket via Starscream and I want to receive a response if it isn't nil.
open func write(string: String, completion: (() -> ())? = nil) {
guard isConnected else { return }
dequeueWrite(string.data(using: String.Encoding.utf8)!, code: .textFrame, writeCompletion: completion)
}
and here is deqeueWrite func:
private func dequeueWrite(_ data: Data, code: OpCode, writeCompletion: (() -> ())? = nil) {
let operation = BlockOperation()
operation.addExecutionBlock { [weak self, weak operation] in
//stream isn't ready, let's wait
guard let s = self else { return }
guard let sOperation = operation else { return }
var offset = 2
var firstByte:UInt8 = s.FinMask | code.rawValue
var data = data
if [.textFrame, .binaryFrame].contains(code), let compressor = s.compressionState.compressor {
do {
data = try compressor.compress(data)
if s.compressionState.clientNoContextTakeover {
try compressor.reset()
}
firstByte |= s.RSV1Mask
} catch {
// TODO: report error? We can just send the uncompressed frame.
}
}
let dataLength = data.count
let frame = NSMutableData(capacity: dataLength + s.MaxFrameSize)
let buffer = UnsafeMutableRawPointer(frame!.mutableBytes).assumingMemoryBound(to: UInt8.self)
buffer[0] = firstByte
if dataLength < 126 {
buffer[1] = CUnsignedChar(dataLength)
} else if dataLength <= Int(UInt16.max) {
buffer[1] = 126
WebSocket.writeUint16(buffer, offset: offset, value: UInt16(dataLength))
offset += MemoryLayout<UInt16>.size
} else {
buffer[1] = 127
WebSocket.writeUint64(buffer, offset: offset, value: UInt64(dataLength))
offset += MemoryLayout<UInt64>.size
}
buffer[1] |= s.MaskMask
let maskKey = UnsafeMutablePointer<UInt8>(buffer + offset)
_ = SecRandomCopyBytes(kSecRandomDefault, Int(MemoryLayout<UInt32>.size), maskKey)
offset += MemoryLayout<UInt32>.size
for i in 0..<dataLength {
buffer[offset] = data[i] ^ maskKey[i % MemoryLayout<UInt32>.size]
offset += 1
}
var total = 0
while !sOperation.isCancelled {
let stream = s.stream
let writeBuffer = UnsafeRawPointer(frame!.bytes+total).assumingMemoryBound(to: UInt8.self)
let len = stream.write(data: Data(bytes: writeBuffer, count: offset-total))
if len <= 0 {
var error: Error?
let errCode = InternalErrorCode.outputStreamWriteError.rawValue
error = s.errorWithDetail("output stream error during write", code: errCode)
s.doDisconnect(error)
break
} else {
total += len
}
if total >= offset {
if let queue = self?.callbackQueue, let callback = writeCompletion {
queue.async {
callback()
}
}
break
}
}
}
writeQueue.addOperation(operation)
}
So right now I can call this function like this:
socket.write(string: frameJSONSring) { () -> Void in
}
But I'd like a response in that handler so that I can read the response data (if there is any) from the socket. Apparently I can pass a custom response handler as a parameter when calling:
socket.write(string: frameJSONSring) { (CUSTOM_HANDLER_HERE) -> Void in
}
open func write(string: String, completion: ((Int) -> ())?) {
guard isConnected else { return }
let someParameter = 5
dequeueWrite(string.data(using: String.Encoding.utf8)!, code: .textFrame, writeCompletion: completion(someParameter))
}
Notice I:
added an Int as a parameter you pass to the handler.
changed completion to completion(someParameter)
You can then use it like such:
socket.write(string: frameJSONSring) { number in
print(number)
}
You can replace the Int with any other type you like.
Also no need to do = nil. When something is an optional then it's already defaulted to nil.
I'm trying to upload a file with Swift.
I found this (poorly written) tutorial from Apple for Objective-C:
I tried to implement it in Swift, but got stuck on dereferencing the pointer in the callback function:
func upload(filepath:String, url:String){
do {
let attr:NSDictionary? = try FileManager.default.attributesOfItem(atPath: filepath) as NSDictionary;
if attr == nil { return; }
let size:UInt64? = attr?.fileSize();
if size==nil { return; }
var info = MyStreamInfo(writeStream: CFWriteStreamCreateWithFTPURL(nil, CFURLCreateWithString(nil,url as CFString, nil)) as! CFWriteStream,
readStream: CFReadStreamCreateWithFile(nil, CFURLCreateWithString(nil, filepath as CFString, nil)),
/*proxyDict: CFDictionary(), */fileSize: size!, totalBytesWritten: 0, leftOverByteCount: 0, buffer: UnsafeMutablePointer<UInt8>.allocate(capacity: bSize));
var myContext=CFStreamClientContext(version: 0, info: &info, retain: nil, release: nil, copyDescription: nil);
//myContext.info = info;
CFReadStreamOpen(info.readStream);
CFWriteStreamSetProperty(info.writeStream, CFStreamPropertyKey(kCFStreamPropertyFTPUserName), username as CFTypeRef);
CFWriteStreamSetProperty(info.writeStream, CFStreamPropertyKey(kCFStreamPropertyFTPPassword), password as CFTypeRef);
CFWriteStreamSetProperty(info.writeStream, CFStreamPropertyKey(kCFStreamPropertyFTPProxyHost), hostname as CFTypeRef);
CFWriteStreamSetProperty(info.writeStream, CFStreamPropertyKey(kCFStreamPropertyFTPProxyPort), port as CFTypeRef);
CFWriteStreamSetClient(info.writeStream, CFStreamEventType.canAcceptBytes.rawValue, writeCB as! CFWriteStreamClientCallBack, &myContext);
} catch let error as NSError {
print(error);
}
}
func writeCB(stream:CFWriteStream, event:CFStreamEventType, myptr: UnsafeMutableRawPointer) {
var info:MyStreamInfo;
var totalBytesRead:Int32 = 0;
repeat {
var bytesRead:Int32 = 0;
var bytesWritten:Int32 = 0;
if info.leftOverByteCount>0 {
bytesRead = Int32(info.leftOverByteCount);
} else {
bytesRead = Int32(CFReadStreamRead(info.readStream, info.buffer, bSize));
if (bytesRead < 0){
print("error");
return;
}
totalBytesRead += bytesRead;
}
bytesWritten = Int32(CFWriteStreamWrite(info.writeStream, info.buffer, CFIndex(bytesRead)));
if bytesWritten > 0 {
info.totalBytesWritten += UInt32(bytesWritten);
if bytesWritten < bytesRead {
info.leftOverByteCount = UInt32(bytesRead - bytesWritten);
memmove(info.buffer, info.buffer.advanced(by: Int(bytesWritten)), Int(info.leftOverByteCount));
} else {
info.leftOverByteCount = 0;
}
} else {
if bytesWritten < 0 {
print("error");
break;
}
}
} while(CFWriteStreamCanAcceptBytes(info.writeStream));
}
How can I get the MyStreamInfo object out of the pointer?
Am I doing the rest right?
username, password, etc are of course members of my class that I set elsewhere.
You can use FilesProvider library to deal with FTP. It doesn't use deprecated CFWriteStreamCreateWithFTPURL API and implements FTP protocol from scratch and provides a high level, FileManager like API to deal with FTP/FTPS.
You can see a sample implementation here.
I just realized that my old app is not working anymore because unsafeAddressOf is abandoned in Swift 3. I have been searching in Apple documentations and online tutorials but still cant figure out how to change my code to be compliant with Swift 3. Here is my code:
import UIKit
import ImageIO
extension UIImage {
public class func gifWithData(data: NSData) -> UIImage? {
guard let source = CGImageSourceCreateWithData(data, nil) else {
print("SwiftGif: Source for the image does not exist")
return nil
}
return UIImage.animatedImageWithSource(source: source)
}
public class func gifWithName(name: String) -> UIImage? {
guard let bundleURL = Bundle.main.url(forResource: name, withExtension: "gif") else {
print("SwiftGif: This image named \"\(name)\" does not exist")
return nil
}
guard let imageData = NSData(contentsOfURL: bundleURL) else {
print("SwiftGif: Cannot turn image named \"\(name)\" into NSData")
return nil
}
return gifWithData(imageData)
}
class func delayForImageAtIndex(index: Int, source: CGImageSource!) -> Double {
var delay = 0.1
// Get dictionaries
let cfProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil)
let gifProperties: CFDictionary = unsafeBitCast(CFDictionaryGetValue(cfProperties, unsafeAddressOf(kCGImagePropertyGIFDictionary)), to: CFDictionary.self)
// Get delay time
var delayObject: AnyObject = unsafeBitCast(
CFDictionaryGetValue(gifProperties,
unsafeAddressOf(kCGImagePropertyGIFUnclampedDelayTime)),
to: AnyObject.self)
if delayObject.doubleValue == 0 {
delayObject = unsafeBitCast(CFDictionaryGetValue(gifProperties,
unsafeAddressOf(kCGImagePropertyGIFDelayTime)), to: AnyObject.self)
}
delay = delayObject as! Double
if delay < 0.1 {
delay = 0.1 // Make sure they're not too fast
}
return delay
}
class func gcdForPair( a: Int?, var _ b: Int?) -> Int {
// Check if one of them is nil
var a = a
if b == nil || a == nil {
if b != nil {
return b!
} else if a != nil {
return a!
} else {
return 0
}
}
// Swap for modulo
if a < b {
let c = a
a = b
b = c
}
// Get greatest common divisor
var rest: Int
while true {
rest = a! % b!
if rest == 0 {
return b! // Found it
} else {
a = b
b = rest
}
}
}
class func gcdForArray(array: Array<Int>) -> Int {
if array.isEmpty {
return 1
}
var gcd = array[0]
for val in array {
gcd = UIImage.gcdForPair(val, gcd)
}
return gcd
}
class func animatedImageWithSource(source: CGImageSource) -> UIImage? {
let count = CGImageSourceGetCount(source)
var images = [CGImage]()
var delays = [Int]()
// Fill arrays
for i in 0..<count {
// Add image
if let image = CGImageSourceCreateImageAtIndex(source, i, nil) {
images.append(image)
}
// At it's delay in cs
let delaySeconds = UIImage.delayForImageAtIndex(index: Int(i),
source: source)
delays.append(Int(delaySeconds * 1000.0)) // Seconds to ms
}
// Calculate full duration
let duration: Int = {
var sum = 0
for val: Int in delays {
sum += val
}
return sum
}()
// Get frames
let gcd = gcdForArray(array: delays)
var frames = [UIImage]()
var frame: UIImage
var frameCount: Int
for i in 0..<count {
frame = UIImage(CGImage: images[Int(i)])
frameCount = Int(delays[Int(i)] / gcd)
for _ in 0..<frameCount {
frames.append(frame)
}
}
// Heyhey
let animation = UIImage.animatedImage(with: frames,
duration: Double(duration) / 1000.0)
return animation
}
}
Does anyone have an idea how I can fix this code?
I would like to detect a specific USB is plugged in/removed in my application. For now, I can get the deviceName with this tutorial Working With USB Device Interfaces. But, how can I do the callback function of (deviceAdded)IOServiceMatchingCallBack in Swift.
I tried as follows, but I got an error: Cannot convert value of type '(UnsafePointer, iterator: io_iterator_t) -> ()' to expected argument type 'IOServiceMatchingCallback!'
func detectUSBEvent() {
var portIterator: io_iterator_t = 0
var kr: kern_return_t = KERN_FAILURE
let matchingDict = IOServiceMatching(kIOUSBDeviceClassName)
let vendorIDString = kUSBVendorID as CFStringRef!
let productIDString = kUSBProductID as CFStringRef!
CFDictionarySetValue(matchingDict, unsafeAddressOf(vendorIDString), unsafeAddressOf(VendorID))
CFDictionarySetValue(matchingDict, unsafeAddressOf(productIDString), unsafeAddressOf(ProductID))
// To set up asynchronous notifications, create a notification port and add its run loop event source to the program’s run loop
let gNotifyPort: IONotificationPortRef = IONotificationPortCreate(kIOMasterPortDefault)
let runLoopSource: Unmanaged<CFRunLoopSource>! = IONotificationPortGetRunLoopSource(gNotifyPort)
let gRunLoop: CFRunLoop! = CFRunLoopGetCurrent()
CFRunLoopAddSource(gRunLoop, runLoopSource.takeUnretainedValue(), kCFRunLoopDefaultMode)
// Notification of first match:
kr = IOServiceAddMatchingNotification(gNotifyPort, kIOFirstMatchNotification, matchingDict, deviceAdded, nil, &portIterator)
deviceAdded(nil, iterator: portIterator)
}
func deviceAdded(refCon: UnsafePointer<Void>, iterator: io_iterator_t) {
if let usbDevice: io_service_t = IOIteratorNext(iterator)
{
let name = String()
let cs = (name as NSString).UTF8String
let deviceName: UnsafeMutablePointer<Int8> = UnsafeMutablePointer<Int8>(cs)
let kr: kern_return_t = IORegistryEntryGetName(usbDevice, deviceName)
if kr == KERN_SUCCESS {
let deviceNameAsCFString = CFStringCreateWithCString(kCFAllocatorDefault, deviceName,
kCFStringEncodingASCII)
print(deviceNameAsCFString)
// if deviceNameAsCFString == XXX
// Do Something
}
}
}
Here's a Swift 3 version, using closures instead of global functions (a closure w/o a context can be bridged to a C function pointer), using GCD instead of Runloops (much nicer API), using callbacks and dispatches to inform about events and using real objects instead of static objects or singletons:
import Darwin
import IOKit
import IOKit.usb
import Foundation
class IOUSBDetector {
enum Event {
case Matched
case Terminated
}
let vendorID: Int
let productID: Int
var callbackQueue: DispatchQueue?
var callback: (
( _ detector: IOUSBDetector, _ event: Event,
_ service: io_service_t
) -> Void
)?
private
let internalQueue: DispatchQueue
private
let notifyPort: IONotificationPortRef
private
var matchedIterator: io_iterator_t = 0
private
var terminatedIterator: io_iterator_t = 0
private
func dispatchEvent (
event: Event, iterator: io_iterator_t
) {
repeat {
let nextService = IOIteratorNext(iterator)
guard nextService != 0 else { break }
if let cb = self.callback, let q = self.callbackQueue {
q.async {
cb(self, event, nextService)
IOObjectRelease(nextService)
}
} else {
IOObjectRelease(nextService)
}
} while (true)
}
init? ( vendorID: Int, productID: Int ) {
self.vendorID = vendorID
self.productID = productID
self.internalQueue = DispatchQueue(label: "IODetector")
guard let notifyPort = IONotificationPortCreate(kIOMasterPortDefault) else {
return nil
}
self.notifyPort = notifyPort
IONotificationPortSetDispatchQueue(notifyPort, self.internalQueue)
}
deinit {
self.stopDetection()
}
func startDetection ( ) -> Bool {
guard matchedIterator == 0 else { return true }
let matchingDict = IOServiceMatching(kIOUSBDeviceClassName)
as NSMutableDictionary
matchingDict[kUSBVendorID] = NSNumber(value: vendorID)
matchingDict[kUSBProductID] = NSNumber(value: productID)
let matchCallback: IOServiceMatchingCallback = {
(userData, iterator) in
let detector = Unmanaged<IOUSBDetector>
.fromOpaque(userData!).takeUnretainedValue()
detector.dispatchEvent(
event: .Matched, iterator: iterator
)
};
let termCallback: IOServiceMatchingCallback = {
(userData, iterator) in
let detector = Unmanaged<IOUSBDetector>
.fromOpaque(userData!).takeUnretainedValue()
detector.dispatchEvent(
event: .Terminated, iterator: iterator
)
};
let selfPtr = Unmanaged.passUnretained(self).toOpaque()
let addMatchError = IOServiceAddMatchingNotification(
self.notifyPort, kIOFirstMatchNotification,
matchingDict, matchCallback, selfPtr, &self.matchedIterator
)
let addTermError = IOServiceAddMatchingNotification(
self.notifyPort, kIOTerminatedNotification,
matchingDict, termCallback, selfPtr, &self.terminatedIterator
)
guard addMatchError == 0 && addTermError == 0 else {
if self.matchedIterator != 0 {
IOObjectRelease(self.matchedIterator)
self.matchedIterator = 0
}
if self.terminatedIterator != 0 {
IOObjectRelease(self.terminatedIterator)
self.terminatedIterator = 0
}
return false
}
// This is required even if nothing was found to "arm" the callback
self.dispatchEvent(event: .Matched, iterator: self.matchedIterator)
self.dispatchEvent(event: .Terminated, iterator: self.terminatedIterator)
return true
}
func stopDetection ( ) {
guard self.matchedIterator != 0 else { return }
IOObjectRelease(self.matchedIterator)
IOObjectRelease(self.terminatedIterator)
self.matchedIterator = 0
self.terminatedIterator = 0
}
}
And here is some simple test code to test that class (set product and vendor ID as appropriate for your USB device):
let test = IOUSBDetector(vendorID: 0x4e8, productID: 0x1a23)
test?.callbackQueue = DispatchQueue.global()
test?.callback = {
(detector, event, service) in
print("Event \(event)")
};
_ = test?.startDetection()
while true { sleep(1) }
It works after I put the callback function out the class. However, I don't know why.
class IODetection {
class func monitorUSBEvent(VendorID: Int, ProductID: Int) {
var portIterator: io_iterator_t = 0
var kr: kern_return_t = KERN_FAILURE
let matchingDict = IOServiceMatching(kIOUSBDeviceClassName)
// Add the VENDOR and PRODUCT IDs to the matching dictionary.
let vendorIDString = kUSBVendorID as CFStringRef!
let productIDString = kUSBProductID as CFStringRef!
CFDictionarySetValue(matchingDict, unsafeAddressOf(vendorIDString), unsafeAddressOf(VendorID))
CFDictionarySetValue(matchingDict, unsafeAddressOf(productIDString), unsafeAddressOf(ProductID))
// To set up asynchronous notifications, create a notification port and add its run loop event source to the program’s run loop
let gNotifyPort: IONotificationPortRef = IONotificationPortCreate(kIOMasterPortDefault)
let runLoopSource: Unmanaged<CFRunLoopSource>! = IONotificationPortGetRunLoopSource(gNotifyPort)
let gRunLoop: CFRunLoop! = CFRunLoopGetCurrent()
CFRunLoopAddSource(gRunLoop, runLoopSource.takeRetainedValue(), kCFRunLoopDefaultMode)
// MARK: - USB in Notification
let observer = UnsafeMutablePointer<Void>(unsafeAddressOf(self))
kr = IOServiceAddMatchingNotification(gNotifyPort,
kIOMatchedNotification,
matchingDict,
deviceAdded,
observer,
&portIterator)
deviceAdded(nil, iterator: portIterator)
// MARK: - USB remove Notification
kr = IOServiceAddMatchingNotification(gNotifyPort,
kIOTerminatedNotification,
matchingDict,
deviceRemoved,
observer,
&portIterator)
deviceRemoved(nil, iterator: portIterator)
}
}
func deviceAdded(refCon: UnsafeMutablePointer<Void>, iterator: io_iterator_t) -> Void {
var kr: kern_return_t = KERN_FAILURE
while case let usbDevice = IOIteratorNext(iterator) where usbDevice != 0 {
let deviceNameAsCFString = UnsafeMutablePointer<io_name_t>.alloc(1)
defer {deviceNameAsCFString.dealloc(1)}
kr = IORegistryEntryGetName(usbDevice, UnsafeMutablePointer(deviceNameAsCFString))
if kr != KERN_SUCCESS {
deviceNameAsCFString.memory.0 = 0
}
let deviceName = String.fromCString(UnsafePointer(deviceNameAsCFString))
print("Device Added: \(deviceName!)")
// Do something if I get the specific device
if deviceName == "YOUR DEVICE" {
/// Your Action HERE
}
IOObjectRelease(usbDevice)
}
}
My problem was that I wasn't using the iterator in my callback function, so that function wasn't even getting called! Seems like strange behaviour to me, but that was my problem.