Not sure how to implement the SMB project into my app - swift

I'm trying to implement SMB into my app and found this https://github.com/amosavian/AMSMB2. I'm not sure how to implement this code into the UI of my app. For example, do I connect the connect function to a button and if so how would I proceed to do so. What would I put into the parameters of connect() when I call it if I call it.
Here's the code from the repository:
import AMSMB2
class SMBClient {
/// connect to: `smb://guest#XXX.XXX.XX.XX/share`
let serverURL = URL(string: "smb://XXX.XXX.XX.XX")!
let credential = URLCredential(user: "guest", password: "", persistence: URLCredential.Persistence.forSession)
let share = "share"
lazy private var client = AMSMB2(url: self.serverURL, credential: self.credential)!
private func connect(handler: #escaping (Result<AMSMB2, Error>) -> Void) {
// AMSMB2 can handle queueing connection requests
client.connectShare(name: self.share) { error in
if let error = error {
handler(.failure(error))
} else {
handler(.success(self.client))
}
}
}
func listDirectory(path: String) {
connect { result in
switch result {
case .success(let client):
client.contentsOfDirectory(atPath: path) { result in
switch result {
case .success(let files):
for entry in files {
print("name:", entry[.nameKey] as! String,
", path:", entry[.pathKey] as! String,
", type:", entry[.fileResourceTypeKey] as! URLFileResourceType,
", size:", entry[.fileSizeKey] as! Int64,
", modified:", entry[.contentModificationDateKey] as! Date,
", created:", entry[.creationDateKey] as! Date)
}
case .failure(let error):
print(error)
}
}
case .failure(let error):
print(error)
}
}
}
func moveItem(path: String, to toPath: String) {
self.connect { result in
switch result {
case .success(let client):
client.moveItem(atPath: path, toPath: toPath) { error in
if let error = error {
print(error)
} else {
print("\(path) moved successfully.")
}
// Disconnecting is optional, it will be called eventually
// when `AMSMB2` object is freed.
// You may call it explicitly to detect errors.
client.disconnectShare(completionHandler: { (error) in
if let error = error {
print(error)
}
})
}
case .failure(let error):
print(error)
}
}
}
}

late but for reference as i couldnt find much when i tried
add the amsmb2 library and that template class file to your project
in the class file set serverurl, share, credential correctly for your smb server
then
declare a variable of class smb_client
then from your button or whatever
var myclient:smb_client=smb_client()
myclient.connect()
to connect to your smb server for example
you can extend that class templete to download/upload items etc
the operations are asynchronous

Related

swift async await pause task and waiting for some data

I need to do infinity load while device not connected to the internet
Example: if user lost the internet connection when the app doing a request then app must waiting while internet connection will restore and trying again to do the request
I do it with the completions
func getData(completion: ((SomeData?) -> Void)?) {
apiService.getSomeData() { [weak self] result in
switch result {
case .success(let data):
return data
case .failure(let error):
switch error {
case .networkError:
let getDataTask: () -> Void = {
self?.getData()
}
// when internet connection will be restore then execute all tasks from onConnectionToTheInternetRestoredTasks
self?.onConnectionToTheInternetRestoredTasks.append(getDataTask)
default:
throw error
}
}
}
}
private func onConnectionToTheInternetRestored() {
onConnectionToTheInternetRestoredTasks.forEach { $0() }
onConnectionToTheInternetRestoredTasks = []
}
but now I need to refactor it in async/await
func getData() async throws -> SomeData {
let result = await apiService.getSomeData()
switch result {
case .success(let data):
return data
case .failure(let error):
switch error {
case .networkError:
// need to do wating for restore and try again
default:
throw error
}
}
}
I don't know how to process .networkError in getData() async throws to waiting for restore connection
Can u please explain it to me ?

Swift Test case Falling with Expectation

I am trying to run test case for Failure response . I have an empty json file into project and named it FailureResponse . This file is empty . I trying to count the number of array is empty for example ..
XCTAssertTrue(schools.count==0)
It should pass the test because the json file is empty .
same result fields like school name and School location etc but the problem is it showing error ..
testFailure(): Asynchronous wait failed: Exceeded timeout of 6 seconds, with unfulfilled expectations: "waiting for response".
View Model code...
import Foundation
import Combine
class ViewModel {
private let networkManager = NetworkManager()
#Published private(set) var school = [School]()
func getSchools() {
loadMoreSchools()
}
func loadMoreSchools() {
let newURL = NetworkURLs.baseURL
networkManager
.getModel([School].self, from: newURL) { [weak self] result in
switch result {
case .success(let schoolResponse):
self?.school = schoolResponse
print(schoolResponse)
case .failure(let error):
print(error)
}
}
}
func getSchoolName(by row: Int) -> String {
let schoolName = school[row]
return schoolName.schoolName.uppercased()
}
func getSchoolLocation(by row: Int) -> String {
return "\(school[row].location)"
}
}
Here is my Mock service call ..
class MockService: NetworkManagerProtocol {
var data: Data?
func getModel<Model>(_ type: Model.Type, from url: String, completion: #escaping (Result<Model, Alomafire_Project.NetworkError>) -> ()) where Model : Decodable, Model : Encodable {
if let data = data {
do {
let result = try JSONDecoder().decode(type, from: data)
completion(.success(result))
} catch (let error){
print(error)
}
}
}
}
Here is code for call the local Jason ..
func getData(json: String) throws -> Data {
guard let url = Bundle(for: Alomafire_ProjectTests.self).url(forResource: json, withExtension: "json")
else { return Data() }
return try Data(contentsOf: url)
}
Here is the test case ....
func testFailure() throws {
// Given
mockService.data = try getData(json: "FailureResponse")
var schools: [School] = []
let expectation = expectation(description: "waiting for response")
// When
viewModel?
.$school
.dropFirst()
.sink(receiveValue: { result in
schools = result
expectation.fulfill()
})
.store(in: &subscribers)
// viewModel?.getSchools()
// Then
waitForExpectations(timeout: 10.0)
XCTAssertTrue(schools.count==0)
}
Here is the debug result . it return 0 ..
Here is the screenshot of the result ..
You mention in the question that "the json file is empty." If that is the case, then this test will fail. The MockService assumes that the Data pulled from the json file will be decodable to the type requested. If it isn't the getModel(_:from:completion:) will never call the completion and the test will not complete in the specified time limit. Solve this by calling the completion closure even when the JSONDecoder response with an error.
Also, even if that mock emits the error properly, your ViewModel doesn't do anything with it that would cause the schools type to update.

Failed to decode data coming from client

I am following Ray Wanderlich's book 'Server Side Swift with Vapor' and I am at chapter 26: Adding profile pictures.
First, I defined this struct:
struct ImageUploadData: Content {
var picture: Data
}
Then, in a route I try to decode it:
func postProfilePictureHandler(_ req: Request) throws -> EventLoopFuture<User> {
let data = try req.content.decode(ImageUploadData.self)
...
From the client side, I use Alamofire:
#discardableResult func uploadProfilePicture(for user: User, data: Data) async throws -> User {
enum Error: LocalizedError {
case missingUserID
}
guard let userID = user.id else {
throw Error.missingUserID
}
let appendix = "\(userID)/profilePicture"
let parameters = [
"picture": data
]
return try await withCheckedThrowingContinuation { continuation in
Task {
AF.request(baseUrl + appendix, method: .post, parameters: parameters).responseData { response in
switch response.result {
case .success(let data):
do {
let user = try JSONDecoder().decode(User.self, from: data)
continuation.resume(returning: user)
} catch {
continuation.resume(throwing: error)
}
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}
}
In my integration tests, I create the picture's data like this:
guard let data = image?.pngData() else {
throw Error.missingPictureData
}
And then I pass it to the above method. The problem is that in the server side, the decoding fails with this error:
The data couldn’t be read because it is missing.
Just to understand if I was doing something else wrong, I tried the above methods with one difference: I replace the type 'Data' with 'String':
struct ImageUploadData: Content {
var picture: String
}
This wouldn't be useful for me because I need a data object, but just as a test to see if this doesn't produce an error, I tried and indeed this is decoded successfully. So I suspect that the problem is in how I encode the data before sending it to the server, but I don't know what's wrong.

Can i make a duplicate class as i can with functions

So i have this code where i'm trying to make an task handler for requests. But in some cases the request doesn't get an model in response and therefor i don't want it to process any data. Hard to explain, but code shown below:
class UserTask<T: Codable>: ExecuteProtocol {
let userType: UserRequests
init(userType: UserRequests) {
self.userType = userType
}
var request: URLRequest {
return userType.build
}
public func run(completion: #escaping ((Response<T, NAError>) ->())) {
executeRequest(request: request) { (response) in
switch response {
case .success(let data):
completion(NADecoder<T>.decode(data: data).model)
break
case .failure(let error):
completion(.failure(error))
break
}
}
}
}
class UserTask: ExecuteProtocol {
let userType: UserRequests
init(userType: UserRequests) {
self.userType = userType
}
var request: URLRequest {
return userType.build
}
public func run(completion: #escaping ((Response<Any?, NAError>) ->())) {
executeRequest(request: request) { (response) in
switch response {
case .success(let data):
completion(.success(nil))
break
case .failure(let error):
completion(.failure(error))
break
}
}
}
}
This of course say Invalid redeclaration of 'UserTask' But can i do this in any smooth way? I have tried making the Codable optional and then unwrapping it. But as i want to keep the type of it in Decodable purpose it doesn't seem to work.
Any suggestions?
There is no need to create multiple classes for same functionality. You simply need to make some changes to a single class to support both your use-cases.
Instead of adding generic <T> to the class UserTask, add it to method run(completion:), i.e.
class UserTask: ExecuteProtocol {
let userType: UserRequests
init(userType: UserRequests) {
self.userType = userType
}
var request: URLRequest {
return userType.build
}
public func run<T: Codable>(type: T.Type, completion: #escaping ((Response<T?, NAError>) ->())) {
//your code here...
}
}
Call it like,
task.run(type: YourType.self) { (response) in
//add your code here...
}

no response from server: unknown: transportError

I am getting the following error when querying my stitch remote collection in Swift: no response from server: unknown: transportError
This is my test query:
//var aircraftCollection: RemoteMongoCollection<Aircraft>!
var aircraftCollection: RemoteMongoCollection<Document>!
var mongoDb = MongoDB()
class MongoDB: ErrorListener {
init() {
do {
let client = try Stitch
.initializeDefaultAppClient(withClientAppID: "....")
let mongoClient = try client.serviceClient(
fromFactory: remoteMongoClientFactory, withName: "...."
)
client.auth.login(withCredential: UserPasswordCredential(withUsername: "....", withPassword: "....")) { result in
switch result {
case .success(let user):
// Get collections from database
aircraftCollection = mongoClient.db("database").collection("aircraft")
let aircraft = Document(dictionaryLiteral: ("_id", ObjectId()))
aircraftCollection.sync.insertOne(document: aircraft, { (result) in
switch result {
case .success(_):
print("inserted successfully")
case .failure(let e):
fatalError(e.localizedDescription)
}
})
aircraftCollection.find().first { result in
switch result {
case .success(let aircraft):
let aircraftList = aircraft.map { $0 }
print(aircraftList)
case .failure(let error):
// this is where error occurs
print(error.localizedDescription)
}
}
case .failure(let error):
print("Error in login: \(error)")
}
}
} catch let error {
print("do catch error")
print(error)
}
}
My connection seems to be fine, I have another stitch app built in JavaScript on localhost that performs the same query without any trouble.
I am using pod 'StitchSDK', '~> 5.0.0'
This shouldn't matter, but are you intentionally trying to utilize mobile sync, or would you rather just call aircraftCollection.insertOne()