Skip to content

Commit

Permalink
feat: implement request using InpuStream on iOS
Browse files Browse the repository at this point in the history
  • Loading branch information
jefft0 committed Oct 25, 2022
1 parent 2af8078 commit 533fd5c
Show file tree
Hide file tree
Showing 6 changed files with 223 additions and 19 deletions.
16 changes: 12 additions & 4 deletions ios/Bridge/GomobileIPFS.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
objects = {

/* Begin PBXBuildFile section */
1AB96057288589CB00AC7579 /* InputStreamFromGo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AB96055288589CB00AC7579 /* InputStreamFromGo.swift */; };
1AB96058288589CB00AC7579 /* InputStreamToGo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1AB96056288589CB00AC7579 /* InputStreamToGo.swift */; };
8AF76E4826EF532E007DF24A /* CoreBluetooth.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8AF76E4626EA36F9007DF24A /* CoreBluetooth.framework */; };
8AF76E4926EF532E007DF24A /* CoreBluetooth.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 8AF76E4626EA36F9007DF24A /* CoreBluetooth.framework */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
8D1B639D24487FC900CE188F /* ConfigIPFSTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D1B639C24487FC900CE188F /* ConfigIPFSTest.swift */; };
Expand Down Expand Up @@ -51,6 +53,8 @@
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
1AB96055288589CB00AC7579 /* InputStreamFromGo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InputStreamFromGo.swift; sourceTree = "<group>"; };
1AB96056288589CB00AC7579 /* InputStreamToGo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InputStreamToGo.swift; sourceTree = "<group>"; };
8AF76E4626EA36F9007DF24A /* CoreBluetooth.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreBluetooth.framework; path = System/Library/Frameworks/CoreBluetooth.framework; sourceTree = SDKROOT; };
8D1B639C24487FC900CE188F /* ConfigIPFSTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigIPFSTest.swift; sourceTree = "<group>"; };
8D2C38A324470D470005C2DD /* RequestIPFSTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestIPFSTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -134,6 +138,8 @@
8DE4ECE72402BFE500B70884 /* Sources */ = {
isa = PBXGroup;
children = (
1AB96055288589CB00AC7579 /* InputStreamFromGo.swift */,
1AB96056288589CB00AC7579 /* InputStreamToGo.swift */,
8DE4ECF92402C0ED00B70884 /* Config.swift */,
8DE4ECFA2402C0ED00B70884 /* Error.swift */,
8DE4ECFB2402C0ED00B70884 /* IPFS.swift */,
Expand Down Expand Up @@ -265,7 +271,9 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
1AB96057288589CB00AC7579 /* InputStreamFromGo.swift in Sources */,
8DE4ED032402C0ED00B70884 /* Repo.swift in Sources */,
1AB96058288589CB00AC7579 /* InputStreamToGo.swift in Sources */,
8DE4ECFF2402C0ED00B70884 /* SockManager.swift in Sources */,
8DE4ED022402C0ED00B70884 /* IPFS.swift in Sources */,
8DE4ED002402C0ED00B70884 /* Config.swift in Sources */,
Expand Down Expand Up @@ -432,7 +440,7 @@
FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../../packages/build/ios/intermediates/core/**";
INFOPLIST_FILE = GomobileIPFS/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand Down Expand Up @@ -467,7 +475,7 @@
FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../../packages/build/ios/intermediates/core/**";
INFOPLIST_FILE = GomobileIPFS/Info.plist;
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand Down Expand Up @@ -495,7 +503,7 @@
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 9F2792GBSY;
INFOPLIST_FILE = GomobileIPFSTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand All @@ -520,7 +528,7 @@
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = 9F2792GBSY;
INFOPLIST_FILE = GomobileIPFSTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand Down
112 changes: 112 additions & 0 deletions ios/Bridge/GomobileIPFS/Sources/InputStreamFromGo.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
//
// InputStreamFromGo.swift
// GomobileIPFS
//
// Created by Antoine Eddi on 19/01/2020.
//

import Foundation
import Core

public class InputStreamFromGoError: IPFSError {
private static var code: Int = 6
private static var subdomain: String = "InputStreamFromGo"

required init(_ description: String, _ optCause: NSError? = nil) {
super.init(description, optCause, InputStreamFromGoError.subdomain, InputStreamFromGoError.code)
}

required init?(coder: NSCoder) {
super.init(coder: coder)
}
}

public class InputStreamFromGo: InputStream {
private var _streamStatus: Stream.Status
private var _streamError: Error?
private weak var _delegate: StreamDelegate?
private var readCloser: CoreReadCloser

init(_ readCloser: CoreReadCloser) {
self._streamStatus = .notOpen
self._streamError = nil
self.readCloser = readCloser
super.init(data: Data())
}

override public var hasBytesAvailable: Bool {
if self.streamStatus == .atEnd || self.streamStatus == .closed {
return false
}
return true
}

override public var streamStatus: Stream.Status {
return _streamStatus
}

override public var streamError: Error? {
return _streamError
}

override public var delegate: StreamDelegate? {
get {
return _delegate
}
set {
_delegate = newValue
}
}

override public func open() {
if self._streamStatus == .notOpen {
self._streamStatus = .open
}
}

override public func close() {
if self._streamStatus != .closed {
self._streamStatus = .closed
try? self.readCloser.close()
}
}

override public func read(_ buffer: UnsafeMutablePointer<UInt8>, maxLength len: Int) -> Int {
if self._streamStatus != .open {
self._streamError = InputStreamFromGoError("stream not opened (\(self._streamStatus))")
return -1
}

self._streamStatus = .reading

// swiftlint:disable todo
// TODO: Use the buffer argument directly without making a copy.
// swiftlint:enable todo
let tmp: NSData? = NSMutableData(length: len)
var read: Int = 0

do {
try self.readCloser.read(tmp as Data?, n: &read)
} catch let err {
if err.localizedDescription == "EOF" {
self._streamStatus = .atEnd
return 0
}

self._streamStatus = .error
self._streamError = err
return -1
}

tmp!.getBytes(buffer, length: read)
self._streamStatus = .open

return read
}

override public func getBuffer(
_ buffer: UnsafeMutablePointer<UnsafeMutablePointer<UInt8>?>,
length len: UnsafeMutablePointer<Int>) -> Bool {
return false
}
}
31 changes: 31 additions & 0 deletions ios/Bridge/GomobileIPFS/Sources/InputStreamToGo.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// InputStreamToGo.swift
// GomobileIPFS
//
// Created by CI Agent on 20/01/2020.
//

import Foundation
import Core

public class InputStreamToGo: NSObject, CoreReaderProtocol {
private var inputStream: InputStream

init(_ inputStream: InputStream) {
self.inputStream = inputStream
self.inputStream.open()
}

public func read(_ buffer: Data?, n len: UnsafeMutablePointer<Int>?) throws {
var read: Int

let bytes = UnsafeMutablePointer<UInt8>(OpaquePointer((buffer! as NSData).bytes))
read = self.inputStream.read(bytes, maxLength: buffer!.count)
len?.initialize(to: read)

if read == 0 && self.inputStream.streamStatus == .atEnd {
self.inputStream.close()
throw NSError(domain: "", code: 0, userInfo: ["NSLocalizedDescription": "EOF"])
}
}
}
22 changes: 17 additions & 5 deletions ios/Bridge/GomobileIPFS/Sources/RequestBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public class RequestBuilder {
case .string(let string):
self.requestBuilder.stringOptions(option, value: string)
case .bytes(let data):
self.requestBuilder.byteOptions(option, value: data)
self.requestBuilder.bytesOptions(option, value: data)
}

return self
Expand Down Expand Up @@ -97,13 +97,25 @@ public class RequestBuilder {
return self
}

/// Sends the request to the underlying go-ipfs node
/// Sends the request to the underlying go-ipfs node and returns an InputStream
/// - Throws: `RequestBuilderError`: If sending the request failed
/// - Returns: An InputStream from which to read the response
/// - seealso: [IPFS API Doc](https://docs.ipfs.io/reference/api/http/)
public func send() throws -> InputStream {
do {
return try InputStreamFromGo(self.requestBuilder.send())
} catch let error as NSError {
throw RequestBuilderError("sending request failed", error)
}
}

/// Sends the request to the underlying go-ipfs node and returns a byte array
/// - Throws: `RequestBuilderError`: If sending the request failed
/// - Returns: A Data object containing the response
/// - seealso: [IPFS API Doc](https://docs.ipfs.io/reference/api/http/)
public func send() throws -> Data? {
public func sendToBytes() throws -> Data? {
do {
return try self.requestBuilder.send()
return try self.requestBuilder.sendToBytes()
} catch let error as NSError {
throw RequestBuilderError("sending request failed", error)
}
Expand All @@ -114,7 +126,7 @@ public class RequestBuilder {
/// - Returns: A dict containing the response
/// - seealso: [IPFS API Doc](https://docs.ipfs.io/reference/api/http/)
public func sendToDict() throws -> [String: Any]? {
guard let res = try self.requestBuilder.send() else {
guard let res = try self.requestBuilder.sendToBytes() else {
return nil
}

Expand Down
57 changes: 49 additions & 8 deletions ios/Bridge/GomobileIPFSTests/RequestIPFSTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,17 @@
//

import XCTest
import CryptoKit
@testable import GomobileIPFS

class RequestIPFSTests: XCTestCase {

private var ipfs: IPFS!
// This CID is the IPFS logo in the Wikipedia mirror. It should exist for a long time.
private var fileUri: String = "/ipfs/bafkreifxaqwd63x4bhjj33sfm3pmny2codycx27jo77it33hkexzrawyma"
private var expectedFileLength: Int = 2940
private var expectedFileSha256: String =
"SHA256 digest: b7042c3f6efc09d29dee4566dec6e34270f02bebe977fe89ef67512f9882d860"

override func setUp() {
do {
Expand All @@ -27,7 +33,7 @@ class RequestIPFSTests: XCTestCase {

guard let resolveResp = try ipfs.newRequest("resolve")
.with(argument: "/ipns/\(domain)")
.sendToDict() else {
.sendToDict() else {
XCTFail("error while casting dict for \"resolve\"")
return
}
Expand Down Expand Up @@ -62,14 +68,49 @@ class RequestIPFSTests: XCTestCase {
}

func testCatFile() throws {
let latestRaw = try ipfs.newRequest("cat")
.with(argument: "/ipns/xkcd.hacdias.com/latest/info.json")
.send()
let response = try ipfs.newRequest("cat")
.with(argument: fileUri)
.sendToBytes()

do {
try JSONSerialization.jsonObject(with: latestRaw!, options: [])
} catch _ {
XCTFail("error while parsing fetched JSON: \(String(decoding: latestRaw! , as: UTF8.self))")
XCTAssertEqual(
expectedFileLength,
response!.count,
"response should have the correct length"
)
XCTAssertEqual(
expectedFileSha256,
SHA256.hash(data: response!).description,
"response should have the correct SHA256"
)
}

func testCatFileStream() throws {
var count = 0
var hasher = SHA256()

if let stream = try? ipfs.newRequest("cat")
.with(argument: fileUri)
.send() {
var buf: [UInt8] = [UInt8](repeating: 0, count: 1000)
stream.open()
while case let len = stream.read(&buf, maxLength: buf.count), len > 0 {
count += len
hasher.update(data: buf[..<len])
}
stream.close()
} else {
XCTFail("error calling ipfs.newRequest")
}

XCTAssertEqual(
expectedFileLength,
count,
"response should have the correct length"
)
XCTAssertEqual(
expectedFileSha256,
hasher.finalize().description,
"response should have the correct SHA256"
)
}
}
4 changes: 2 additions & 2 deletions ios/Example/Example/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ class ViewController: UIViewController {
do {
let fetchedData = try ViewController.ipfs!.newRequest("cat")
.with(argument: code)
.send()
.sendToBytes()

title = "IPFS File"
image = UIImage(data: fetchedData!)!
Expand Down Expand Up @@ -254,7 +254,7 @@ class ViewController: UIViewController {

let fetchedData = try ViewController.ipfs!.newRequest("cat")
.with(argument: "\(ViewController.XKCDIPNS)/\(formattedIndex)/image.\(imgExt)")
.send()
.sendToBytes()

title = "\(randomIndex). \(fetchedInfo["title"] as! String)"
image = UIImage(data: fetchedData!)!
Expand Down

0 comments on commit 533fd5c

Please sign in to comment.