How do you pass data between operations in swift - swift

I am trying to learn swift. I am currently looking at operations. I can queue up a series of operations and they run. Where I run into a problem is trying to pass data between operations. My getVOOCSVData operation retrieves the correct data which I can confirm by placing a print statement in its completion block. Unfortunately, I cannot figure out how to pass this retrieved data onto the next operation. The next operation (convertStringToArray) runs but the data I attempt to pass in is empty. Below are my operation setup and ConvertStringToArray2 class which is assigned to the convertStringToArray variable.
// operation setup
struct VOOModel {
init() {
let operationQueue = OperationQueue()
let checkForUpdatedVOOData = IsUpdatedVOODataAvailable()
checkForUpdatedVOOData.completionBlock = {
print("Updated CSV Data = \(checkForUpdatedVOOData.updatedCSVData)")
print("Latest Date = \(checkForUpdatedVOOData.latestCSVDate)")
}
let getVOOCSVData = GetVOOCSVData()
getVOOCSVData.addDependency(checkForUpdatedVOOData)
getVOOCSVData.completionBlock = {
print("completion block get voo csv data")
print("\(getVOOCSVData.csvData)") // prints out all of the data correctly
}
let convertStringToArray = ConvertStringToArray2(csvData: getVOOCSVData.csvData)
convertStringToArray.addDependency(getVOOCSVData)
convertStringToArray.completionBlock = {
print("completion block convert string to array")
}
operationQueue.addOperations([checkForUpdatedVOOData, getVOOCSVData, convertStringToArray], waitUntilFinished: true)
} // end init
} // end structure
// operation class
final class ConvertStringToArray2: Operation {
var localData: String
var sortedDataArray : [IndexFund] = []
init(csvData: String) {
localData = csvData
}
override func main() {
var dataArray = [IndexFund]()
var rows = localData.components(separatedBy: "\n") // gives rows a value of 1
rows.removeFirst()
for row in rows {
let columns = row.components(separatedBy: ",")
if columns.count == 6 {
let theDate = columns[0].replacingOccurrences(of: "-", with: "")
let timeStamp = stringDateFormatter.date(from: theDate)!
let open = Double(columns[1])!
let high = Double(columns[2])!
let low = Double(columns[3])!
let close = Double(columns[4])!
let theVolume = columns[5].trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
let volume = Int64(theVolume)
let fundValuesByDay = IndexFund(
timeStamp: timeStamp,
open: open,
high: high,
low: low,
close: close,
volume: volume!)
dataArray.append(fundValuesByDay)
}
sortedDataArray = dataArray.sorted {
$0.timeStamp < $1.timeStamp
}
}
}
}

After some additional research I found that a block operation allows me to pass data between operations. Below is the updated code. It all runs in the init of my model as above. The link below is the site used to understand how to utilize the block operation.
https://medium.com/#marcosantadev/4-ways-to-pass-data-between-operations-with-swift-2fa5b3a3d561
let operationQueue = OperationQueue()
let updatedVOOData = IsUpdatedVOODataAvailable()
updatedVOOData.completionBlock = {
if updatedVOOData.updatedCSVData == true {
let fetchVOO = GetVOOCSVData()
let updateVOO = UpdateVOOPersistentStore()
let vooAdapter = BlockOperation() { [unowned updateVOO, unowned fetchVOO] in
updateVOO.dataArray = fetchVOO.dataArray
}
vooAdapter.addDependency(fetchVOO)
updateVOO.addDependency(vooAdapter)
operationQueue.addOperations([fetchVOO, updateVOO, vooAdapter], waitUntilFinished: true)
} // end if block
print("Updated CSV Data = \(updatedVOOData.updatedCSVData)")
print("Latest Date = \(updatedVOOData.latestCSVDate)")
} // end completion block
operationQueue.addOperations([updatedVOOData], waitUntilFinished: true)

Related

How to empty an appended array after a widget timeline update

I am transitioning from UIKit to SwiftUI in my app and am updating network calls to async await as well. I created a Large size widget that displays the weather for 7 airports. The airports identifiers are stored in an app group shared userdefatults container. I update the timeline every minute (just for testing, normally it would be every 20 minutes). Initially when the widget is selected and appears, all data is there and correct. After a timeline update the data updates, but not all the airports are returned and after two updates or so (not consistent), the screen goes blank. The userdefaults airports are updated from the main app and saved in the shared user defaults container and it calls WidgetCenter.shared.reloadAllTimelines. This is all working fine as I have another process that uses the same container for a small widget, but with only one airport returning data without the need for an appended array. If I remove the calls to empty the array, the data remains and doesn't go blank, but of course the array keeps appending. I've tried the removeAll() and [] to empty the array at different places in the code, but same result. I am trying to understand the flow in the async/await calls, but seem to be missing something here? Any help would be greatly appreciated. I've been googling and searching stack overflow for a month and don't really know how to solve this issue. Thanks in advance!
actor MetarService: NSObject, XMLParserDelegate, URLSessionDelegate, ObservableObject {
enum MetarFetcherError: Error {
case invalidServerResponse
case missingData
}
#Published var metarArray = [String]()
#Published var metarDataModel: [MetarDataModel] = []
var tempDataModel: [MetarDataModel] = []
func fetchMetars(metarAPTs: String) async throws -> [MetarDataModel] {
let wxUrl = URL(string: "https://www.aviationweather.gov/adds/dataserver_current/httpparam?dataSource=metars&requestType=retrieve&format=xml&hoursBeforeNow=3&mostRecent=true&stationString=" + metarAPTs)!
let (data, response) = try await URLSession.shared.data(from: wxUrl)
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
throw MetarFetcherError.invalidServerResponse
}
guard let xml = SWXMLHash.parse(data) as XMLIndexer? else {
throw MetarFetcherError.missingData
}
noDataResponse = (xml["response"]["data"].element?.attribute(by: "num_results")?.text) ?? "0"
if noDataResponse == "1" && (xml["response"]["data"]["METAR"]["observation_time"].element?.text) != nil {
if (xml["response"]["data"]["METAR"]["station_id"].element?.text) != nil {
myairport = xml["response"]["data"]["METAR"]["station_id"].element!.text
} else {
myairport = "MSNG"
}
if (xml["response"]["data"]["METAR"]["flight_category"].element?.text) != nil {
myfltcat = xml["response"]["data"]["METAR"]["flight_category"].element!.text
} else {
myfltcat = "MISNG"
}
switch myfltcat {
case "VFR":
mymetarImage = "sun.max.circle.fill"
case "MVFR":
mymetarImage = "cloud.sun.circle.fill"
case "IFR":
mymetarImage = "cloud.fog.circle.fill"
case "LIFR":
mymetarImage = "smoke.circle.fill"
default:
mymetarImage = "person.crop.circle.badge.questionmark"
}
if (xml["response"]["data"]["METAR"]["observation_time"].element?.text) != nil {
myobstime = xml["response"]["data"]["METAR"]["observation_time"].element!.text as NSString
if myobstime.length < 16 {
myobstime = "MISNG"
} else {
myobstime = myobstime.substring(with: NSRange(location: 11, length: 5)) as NSString
}
}
if (xml["response"]["data"]["METAR"]["visibility_statute_mi"].element?.text) != nil {
myvis = xml["response"]["data"]["METAR"]["visibility_statute_mi"].element!.text
let intVis = (myvis as NSString) .integerValue
myvis = String(intVis) + "SM"
} else {
myvis = "0"
}
if (xml["response"]["data"]["METAR"]["wind_dir_degrees"].element?.text) != nil {
mywinddir = xml["response"]["data"]["METAR"]["wind_dir_degrees"].element!.text
if mywinddir.contains("VRB") {
mywinddir = "VRB"
} else
if mywinddir.count <= 2 && mywinddir.count > 0 {
mywinddir = "0" + mywinddir
}
} else {
mywinddir = "MISNG"
}
if (xml["response"]["data"]["METAR"]["wind_speed_kt"].element?.text) != nil {
mywindspd = xml["response"]["data"]["METAR"]["wind_speed_kt"].element!.text
if mywindspd == "0" {
mywind = "Calm"
} else if mywindspd.count == 1 {
mywindspd = "0" + mywindspd
mywind = mywinddir + "/" + mywindspd + "KT"
} else if mywindspd.count > 1 {
mywind = mywinddir + "/" + mywindspd + "KT"
}
} else {
mywind = "MISNG"
}
}
self.tempDataModel.append(MetarDataModel(metarImage: mymetarImage, mairport: myairport, mobstime: myobstime as String, mfltcat: myfltcat, mvis: myvis, mwind: mywind))
self.metarDataModel = self.tempDataModel
tempDataModel = []
return metarDataModel
}
func readMetarApts() -> [String] {
let defaults = UserDefaults(suiteName: "group.userdefaults.shared.FRAT")!
if ((defaults.value(forKey: "icaoIdent") as! String).isEmpty) {
defaultairport = "KSFO"
} else {
defaultairport = defaults.value(forKey: "icaoIdent") as! String
}
wxAirport1 = defaults.value(forKey: "wxAirport1") as! String
wxAirport2 = defaults.value(forKey: "wxAirport2") as! String
wxAirport3 = defaults.value(forKey: "wxAirport3") as! String
wxAirport4 = defaults.value(forKey: "wxAirport4") as! String
wxAirport5 = defaults.value(forKey: "wxAirport5") as! String
wxAirport6 = defaults.value(forKey: "wxAirport6") as! String
metarArray.append(defaultairport)
metarArray.append(wxAirport1)
metarArray.append(wxAirport2)
metarArray.append(wxAirport3)
metarArray.append(wxAirport4)
metarArray.append(wxAirport5)
metarArray.append(wxAirport6)
metarArray = metarArray.sorted()
let returnArray = metarArray
metarArray = []
return returnArray
}// end of readAirports function
nonisolated func getAirports() -> ([MetarDataModel] ){
// transData = []
Task{
let tempArray = await readMetarApts()
for apts in tempArray {
let zData = try await self.fetchMetars(metarAPTs: apts)
if zData .isEmpty {
let errorData = MetarDataModel(metarImage: "sun.max.circle.fill", mairport: "DATA", mobstime: "CHK", mfltcat: "MSNG", mvis: "WiFi", mwind: "")
tempData = [errorData]
} else {
transData.append(contentsOf: zData)
tempData = transData
} // else Closure
} //Task Closure
//transData.removeAll()
} // apts in tempArray Closure
tempData = transData
// transData = []
return tempData.sorted()
} // end of getAirports function
} // end of MetarService Class
I have tried different solutions found on stack overflow, reddit, medium and others. But no matter what approach I take, if I try and empty the appended array in preparation for the next timeline update, it will always eventually return without data. At first I thought it was the app group shared container losing reference as I got the much debated 'kCFPreferencesAnyUser, ByHost: Yes, Container: (null)): Using kCFPreferencesAnyUser with a container is only allowed for System Containers, detaching from cfprefsd' in the log, but Apple says this does not indicate that particular condition? And, I use the container elsewhere with no issues. I am new to the async await and the new URLSession.shared.data(from: ) and maybe I'm not understanding the flow and timing of how the data is fetched and returned? I just need to append the array, display the data, then empty the array and wait for the next timeline to fetch new data. I've put the removeAll() and tried [] as an alternative in many different places in my code (at the start of the function and at the end of the function). Stumped!!

Web3Swift Interaction with custom ABI

I am trying to interact with a smart contract I set up.
Basically the goal is to set from an iOS App 5 Parameters
projectTitle
projectLocation
projectStart
projectEnd
teamType
I want the user to set those parameters and write it on the ropsten testnetwork.
I also would like to get the contract information at a later point whenever the user feels for it.
my solidity code is working properly in remix and the contract is already deployed:
pragma solidity >=0.4.22 <0.7.0;
contract ProjectContent {
string public projectTitle;
string public projectLocation;
string public projectStart;
string public projectEnd;
string public teamType;
function projectContent(string initialProjectTitle, string initialProjectLocation, string initialProjectStart, string initialProjectEnd, string initialTeamType) public {
projectTitle = initialProjectTitle;
projectLocation = initialProjectLocation;
projectStart = initialProjectStart;
projectEnd = initialProjectEnd;
teamType = initialTeamType;
}
function setContract(string newProjectTitle, string newProjectLocation, string newProjectStart, string newProjectEnd, string newTeamType) public {
projectTitle = newProjectTitle;
projectLocation = newProjectLocation;
projectStart = newProjectStart;
projectEnd = newProjectEnd;
teamType = newTeamType;
}
function getProjectTitle() public view returns (string) {
return projectTitle;
}
function getProjectLocation() public view returns (string) {
return projectLocation;
}
function getProjectStart() public view returns (string) {
return projectStart;
}
function getProjectEnd() public view returns (string) {
return projectEnd;
}
function getTeamType() public view returns (string) {
return teamType;
}
}
My problem now is that I cannot figure out how to retrieve the data from the blockchain using the web3swift library. I am doing it like so now:
class ProjectContractViewController: UIViewController, HalfModalPresentable {
#IBOutlet weak var contractABIView: UITextView!
var halfModalTransitioningDelegate: HalfModalTransitioningDelegate?
var contractABI = "[{\"constant\":false,\"inputs\":[{\"name\":\"initialProjectTitle\",\"type\":\"string\"},{\"name\":\"initialProjectLocation\",\"type\":\"string\"},{\"name\":\"initialProjectStart\",\"type\":\"string\"},{\"name\":\"initialProjectEnd\",\"type\":\"string\"},{\"name\":\"initialTeamType\",\"type\":\"string\"}],\"name\":\"projectContent\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"newProjectTitle\",\"type\":\"string\"},{\"name\":\"newProjectLocation\",\"type\":\"string\"},{\"name\":\"newProjectStart\",\"type\":\"string\"},{\"name\":\"newProjectEnd\",\"type\":\"string\"},{\"name\":\"newTeamType\",\"type\":\"string\"}],\"name\":\"setContract\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"getProjectEnd\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"getProjectLocation\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"getProjectStart\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"getProjectTitle\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"getTeamType\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"projectEnd\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"projectLocation\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"projectStart\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"projectTitle\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"teamType\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]"
let str = "0x6080604052600436106100ba576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806323a35e62146100bf57806337a4fc7d1461014f5780634a5736fd146101df5780634b04811e1461026f5780634e9d1281146102ff57806363afee221461038f578063775e6d451461051057806393ee0402146105a0578063c3e20c9f14610630578063d8045412146106c0578063dad375ff14610750578063f020cd19146108d1575b600080fd5b3480156100cb57600080fd5b506100d4610961565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101145780820151818401526020810190506100f9565b50505050905090810190601f1680156101415780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561015b57600080fd5b50610164610a03565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101a4578082015181840152602081019050610189565b50505050905090810190601f1680156101d15780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156101eb57600080fd5b506101f4610aa1565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610234578082015181840152602081019050610219565b50505050905090810190601f1680156102615780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561027b57600080fd5b50610284610b3f565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156102c45780820151818401526020810190506102a9565b50505050905090810190601f1680156102f15780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561030b57600080fd5b50610314610bdd565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610354578082015181840152602081019050610339565b50505050905090810190601f1680156103815780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561039b57600080fd5b5061050e600480360381019080803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290505050610c7b565b005b34801561051c57600080fd5b50610525610cf5565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561056557808201518184015260208101905061054a565b50505050905090810190601f1680156105925780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156105ac57600080fd5b506105b5610d97565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156105f55780820151818401526020810190506105da565b50505050905090810190601f1680156106225780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561063c57600080fd5b50610645610e39565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561068557808201518184015260208101905061066a565b50505050905090810190601f1680156106b25780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156106cc57600080fd5b506106d5610ed7565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156107155780820151818401526020810190506106fa565b50505050905090810190601f1680156107425780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561075c57600080fd5b506108cf600480360381019080803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290505050610f79565b005b3480156108dd57600080fd5b506108e6610ff3565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561092657808201518184015260208101905061090b565b50505050905090810190601f1680156109535780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b606060028054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156109f95780601f106109ce576101008083540402835291602001916109f9565b820191906000526020600020905b8154815290600101906020018083116109dc57829003601f168201915b5050505050905090565b60028054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a995780601f10610a6e57610100808354040283529160200191610a99565b820191906000526020600020905b815481529060010190602001808311610a7c57829003601f168201915b505050505081565b60018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b375780601f10610b0c57610100808354040283529160200191610b37565b820191906000526020600020905b815481529060010190602001808311610b1a57829003601f168201915b505050505081565b60048054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610bd55780601f10610baa57610100808354040283529160200191610bd5565b820191906000526020600020905b815481529060010190602001808311610bb857829003601f168201915b505050505081565b60038054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610c735780601f10610c4857610100808354040283529160200191610c73565b820191906000526020600020905b815481529060010190602001808311610c5657829003601f168201915b505050505081565b8460009080519060200190610c91929190611095565b508360019080519060200190610ca8929190611095565b508260029080519060200190610cbf929190611095565b508160039080519060200190610cd6929190611095565b508060049080519060200190610ced929190611095565b505050505050565b606060018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610d8d5780601f10610d6257610100808354040283529160200191610d8d565b820191906000526020600020905b815481529060010190602001808311610d7057829003601f168201915b5050505050905090565b606060048054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610e2f5780601f10610e0457610100808354040283529160200191610e2f565b820191906000526020600020905b815481529060010190602001808311610e1257829003601f168201915b5050505050905090565b60008054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610ecf5780601f10610ea457610100808354040283529160200191610ecf565b820191906000526020600020905b815481529060010190602001808311610eb257829003601f168201915b505050505081565b606060038054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610f6f5780601f10610f4457610100808354040283529160200191610f6f565b820191906000526020600020905b815481529060010190602001808311610f5257829003601f168201915b5050505050905090565b8460009080519060200190610f8f929190611095565b508360019080519060200190610fa6929190611095565b508260029080519060200190610fbd929190611095565b508160039080519060200190610fd4929190611095565b508060049080519060200190610feb929190611095565b505050505050565b606060008054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561108b5780601f106110605761010080835404028352916020019161108b565b820191906000526020600020905b81548152906001019060200180831161106e57829003601f168201915b5050505050905090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106110d657805160ff1916838001178555611104565b82800160010185558215611104579182015b828111156111035782518255916020019190600101906110e8565b5b5090506111119190611115565b5090565b61113791905b8082111561113357600081600090555060010161111b565b5090565b905600a165627a7a72305820458843a936d80ffe49dddb0955a0c1d56d0e15f994cd5ce31b386188b2724a790029"
var contractAddress = EthereumAddress("0x11A0c067d7481240dCA57457eff77fc98dEAdE0F")
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
func callContract(Password: String) {
// Get from address from private key
let formattedKey = Password.trimmingCharacters(in: .whitespacesAndNewlines)
let dataKey = Data.fromHex(formattedKey )!
// ### use [passKey]
let keystore = try! EthereumKeystoreV3(privateKey: dataKey, password: "")!
let keyData = try! JSONEncoder().encode(keystore.keystoreParams)
// let address = keystore.addresses!.first!.address
let address = keystore.addresses!.first!.address
let ethAddress = EthereumAddress(address)
let infura = Web3.InfuraMainnetWeb3()
// 1
let contract = infura.contract(contractABI, at: contractAddress, abiVersion: 2)
// 2
var options = TransactionOptions.defaultOptions
options.from = keystore.addresses!.first!
// 3
let data = Data.init(hex: str)
let transactionIntermediate = contract?.method("getProjectTitle", parameters: [address] as [AnyObject], extraData: data, transactionOptions: options)
// 4
let result = transactionIntermediate!.call(transactionOptions: options)
switch result {
// 5
case .success(let res):
let ans = res["0"] as! Bool
DispatchQueue.main.async {
completion(Result.Success(ans))
}
case .failure(let error):
DispatchQueue.main.async {
completion(Result.Error(error))
}
}
}
}
I get an error for the resultsaying:
"Call can throw, but it is not marked with 'try' and the error is not handled"
and in general I find it really hard to set up the interaction with a smart contract abi.
I am already using the web3swift functionality for sending transactions at it works like a charm.
Maybe someone knows how I can record information on the blockchain and get it using web3swift.
You are close. Starting with the error "Call can throw, but it is not marked with 'try' and the error is not handled" this is caused by trying to call a contract function without using the Try Catch pattern. Do to the way web3 library is designed this pattern is necessary for all write and call methods.
// Incorrect
let result = transactionIntermediate!.call(transactionOptions: options)
// Correct
do {
let result = try transactionIntermediate!.call(transactionOptions: options)
}catch{
print("Error trying to call method \(error)")
}
Additionally, I recommend using the DispatchQueue.main.async along with Promise Kit library when making your contract calls.
ABIs are hard to read and messy, don't recommend using it to help find callable methods and parameters within the contract. Instead I would have the contract open along side Xcode and through the use of either an enum or struct containing all the contract methods that are going to be used.
// Methods available within the contract
enum ContractMethods:String {
case projectContract = "projectContent"
case setContract = "setContract"
case getProjectTitle = "getProjectTitle"
case getProjectLocation = "getProjectLocation"
case getProjectStart = "getProjectStart"
case getProjectEnd = "getProjectEnd"
case getTeamType = "getTeamType"
}
// Usage
ContractMethods.setContract.rawValue
I moved the ABI to a separate file within xcode to keep it clean. Here is link to the file.
Here is a good example to help get you started. Check out my GitHub repo for the improved version.
import UIKit
import web3swift
import PromiseKit
struct Wallet {
let address: String
let data: Data
let name:String
let isHD:Bool
}
struct HDKey {
let name:String?
let address:String
}
var password = "" // leave empty for ganache or use your wallet password
let privateKey = "<PrivateKey>" // Private key of wallet
let walletName = "MyWallet"
let contractAddress = "<ContractAddress>" // 0x11A0c067d7481240dCA57457eff77fc98dEAdE0F
let endpoint = URL(string:"http://127.0.0.1:7545")! // Im using Ganache but it might look like endpoint = URL(string:"https://rinkeby.infura.io/v3/<APIKEY>")!
let abiVersion = 2
class ViewController: UIViewController {
// Mock data used within contract
let projectTitle = "HouseSiding"
let projectLocation = "299 Race Ave. Dacula, GA 30019"
let projectStart = "May 14, 2021"
let projectEnd = "June 15, 2021"
let teamType = "Collaboration"
var web3:web3?
var contract:web3.web3contract?
override func viewDidLoad() {
super.viewDidLoad()
// 1. Create wallet using a private key
let formattedKey = privateKey.trimmingCharacters(in: .whitespacesAndNewlines)
let dataKey = Data.fromHex(formattedKey)!
let keyStore = try! EthereumKeystoreV3(privateKey:dataKey, password: password)!
let keyData = try! JSONEncoder().encode(keyStore.keystoreParams)
let address = keyStore.addresses!.first!.address
let wallet = Wallet(address: address, data: keyData, name: walletName, isHD: false)
// 2. Construct web3 and keystoreManager
do {
web3 = try Web3.new(endpoint)
let data = wallet.data
var keystoreManager: KeystoreManager
if wallet.isHD {
let keystore = BIP32Keystore(data)!
keystoreManager = KeystoreManager([keystore])
}else{
let keystore = EthereumKeystoreV3(data)!
keystoreManager = KeystoreManager([keystore])
}
print(keystoreManager.addresses)
web3!.addKeystoreManager(keystoreManager)
let ethContractAddress = EthereumAddress(contractAddress, ignoreChecksum: true)!
contract = web3!.contract(contractABI, at: ethContractAddress, abiVersion: abiVersion)!
}catch{
print ("Failed to construct contract and/or keystoreManager \(error)")
}
// 3. Create and callout a contract method
//let parameters = [projectTitle,projectLocation,projectStart,projectEnd,teamType] as [AnyObject] // parameters used to created a new project
let parameters = [] as [AnyObject] // no parameters
let response = Promise<Any> { seal in
DispatchQueue.global().async {
// Catch errors within async call
do {
// No extra data for method call
let extraData: Data = Data()
// Options for method call
var options = TransactionOptions.defaultOptions
options.from = EthereumAddress(wallet.address)! // current wallet address
// Leave automatic for gas
options.gasPrice = .automatic
options.gasLimit = .automatic
// Calling get Project title from contract
// NOTE: First call setContract with parameters
let tx = self.contract!.method("getProjectTitle",
parameters: parameters,
extraData: extraData,
transactionOptions: options)
// Depending on the type of call a password might be needed
//if password != nil {
//let result = try tx!.send(password: password)
// seal.resolve(.fulfilled(true))
//}else{
let result = try tx!.call()
// fulfill are result from contract
let anyResult = result["0"] as Any
seal.resolve(.fulfilled(anyResult))
//}
}catch {
// error
seal.reject(error)
}
}
}
response.done({result in
print(result) // Optional(HouseSiding)
})
}
}

deinitialize() was obsoleted in swift 5.0

I was using a pod for ftp picture upload issue. But it is giving an error after I build the app with Swift 5.0.
Here is the error:
deinitialize()' is unavailable: the default argument to deinitialize(count:) has been removed, please specify the count explicitly.
Here is the Swift file belongs to pod:
import Foundation
/* Resource type, values defined in `sys/dirent.h`. */
public enum ResourceType: String {
case Unknown = "Unknown" // DT_UNKNOWN
case Directory = "Directory" // DT_DIR
case RegularFile = "RegularFile" // DT_REG
case SymbolicLink = "SymbolicLink" // DT_LNK
case NamedPipe = "NamedPipe" // DT_FIFO
case CharacterDevice = "CharacterDevice" // DT_CHR
case BlockDevice = "BlockDevice" // DT_BLK
case LocalDomainSocket = "LocalDomainSocket" // DT_SOCK
case Whiteout = "Whiteout" // DT_WHT
}
open class ResourceItem: CustomStringConvertible {
open var type: ResourceType = .Unknown
open var name: String = ""
open var link: String = ""
open var date: Date = Date()
open var size: Int = 0
open var mode: Int = 0
open var owner: String = ""
open var group: String = ""
open var path: String = "/"
open var description: String {
get {
return "\nResourceItem: \(name), \(type.rawValue)"
}
}
}
private let _resourceTypeMap: [Int:ResourceType] = [
Int(DT_UNKNOWN): ResourceType.Unknown,
Int(DT_FIFO): ResourceType.NamedPipe,
Int(DT_SOCK): ResourceType.LocalDomainSocket,
Int(DT_CHR): ResourceType.CharacterDevice,
Int(DT_DIR): ResourceType.Directory,
Int(DT_BLK): ResourceType.BlockDevice,
Int(DT_REG): ResourceType.RegularFile,
Int(DT_LNK): ResourceType.SymbolicLink,
Int(DT_WHT): ResourceType.Whiteout
]
/** Operation for resource listing. */
internal class ResourceListOperation: ReadStreamOperation {
fileprivate var inputData: NSMutableData?
var resources: [ResourceItem]?
override func streamEventEnd(_ aStream: Stream) -> (Bool, NSError?) {
var offset = 0
let bytes = self.inputData!.bytes.bindMemory(to: UInt8.self, capacity: (self.inputData?.length)!)
let totalBytes = CFIndex(self.inputData!.length)
var parsedBytes = CFIndex(0)
let entity = UnsafeMutablePointer<Unmanaged<CFDictionary>?>.allocate(capacity: 1)
var resources = [ResourceItem]()
repeat {
parsedBytes = CFFTPCreateParsedResourceListing(nil, bytes.advanced(by: offset), totalBytes - offset, entity)
if parsedBytes > 0 {
let value = entity.pointee?.takeUnretainedValue()
if let fptResource = value {
resources.append(self.mapFTPResources(fptResource))
}
offset += parsedBytes
}
} while parsedBytes > 0
self.resources = resources
entity.deinitialize()
return (true, nil)
}
fileprivate func mapFTPResources(_ ftpResources: NSDictionary) -> ResourceItem {
let item = ResourceItem()
if let mode = ftpResources[kCFFTPResourceMode as String] as? Int {
item.mode = mode
}
if let name = ftpResources[kCFFTPResourceName as String] as? String {
// CFFTPCreateParsedResourceListing assumes that teh names are in MacRoman.
// To fix it we create data from string and read it with correct encoding.
// https://devforums.apple.com/message/155626#155626
if configuration.encoding == String.Encoding.macOSRoman {
item.name = name
} else if let nameData = name.data(using: String.Encoding.macOSRoman) {
if let encodedName = NSString(data: nameData, encoding: self.configuration.encoding.rawValue) {
item.name = encodedName as String
}
}
item.path = self.path! + item.name
}
if let owner = ftpResources[kCFFTPResourceOwner as String] as? String {
item.owner = owner
}
if let group = ftpResources[kCFFTPResourceGroup as String] as? String {
item.group = group
}
if let link = ftpResources[kCFFTPResourceLink as String] as? String {
item.link = link
}
if let size = ftpResources[kCFFTPResourceSize as String] as? Int {
item.size = size
}
if let type = ftpResources[kCFFTPResourceType as String] as? Int {
if let resourceType = _resourceTypeMap[type] {
item.type = resourceType
}
}
if let date = ftpResources[kCFFTPResourceModDate as String] as? Date {
item.date = date
}
return item
}
override func streamEventHasBytes(_ aStream: Stream) -> (Bool, NSError?) {
if let inputStream = aStream as? InputStream {
let buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: 1024)
let result = inputStream.read(buffer, maxLength: 1024)
if result > 0 {
if self.inputData == nil {
self.inputData = NSMutableData(bytes: buffer, length: result)
} else {
self.inputData!.append(buffer, length: result)
}
}
buffer.deinitialize()
}
return (true, nil)
}
}
Can you help me how can I fix these 2 below lines:
buffer.deinitialize()
entity.deinitialize()
And is it okay if we fix these two lines? I mean does the pod work after we fix these two lines?
deinitialize now requires a count parameter indicating how many values you want to deinitialise.
From the context, the code is probably trying to deinitialise everything the pointer references, so the number of values we deinitialise will be equal to the number of values we allocate. This will be 1024 for buffer and 1 for entity.
You should replace those lines with:
buffer.deinitialize(count: 1024)
// and
entity.deinitialize(count: 1)
respectively
However, since this is code from a pod that you are modifying, make sure to check the terms in the licence of the pod to make sure you are not violating anything.
You should also inform the author of the pod that the pod needs updating. This API change is made in Swift 4.1, I think, so it's quite old.

Realm/Swift: compilation error: Missing argument for parameter 'forPrimaryKey' in call

I'm trying to get my head around swift & realm, so I've created some kind of a test pad programme.
My model is defined like so
class RealmRecord: Object {
// properties
dynamic var id: Int = 0;
dynamic var text: String = ""
dynamic var var1: Double = 0.0
dynamic var var2: Int = 0
dynamic var var3: Double = 0.0
dynamic var var4: Int = 0
dynamic var cdate: Date = Date()
dynamic var cusr: String = ""
dynamic var mdate: Date = Date.distantPast
dynamic var musr: String = ""
dynamic var mcnt: Int = 0
// methods
convenience init(id: Int? = 0, text: String? = "", var1: Double? = 0.0,
var2: Int? = 0, var3: Double? = 0.0, var4: Int? = 0,
cusr: String? = "") {
self.init()
self.id = id!
self.text = text!
self.var1 = var1!
self.var2 = var2!
self.var3 = var3!
self.var4 = var4!
self.cdate = Date()
self.cusr = cusr!
self.mdate = Date.distantPast
self.musr = ""
self.mcnt = 0
} // init
override static func primaryKey() -> String? {
return "id"
} // primaryKey
} // RealmRecord
Persisting the data is accomplished by
try recRealm?.write {
recRealm?.add(self.rec, update: true)
} // try
But when adding the data retrieval via
if let inrec = self.recRealm?.object(RealmRecord.self) {
return inrec
} else {
return List<RealmRecord>()
} // if/else
I'm receiving an error message while compiling the code, reading
DataRealmRecord.swift:84:39: Missing argument for parameter 'forPrimaryKey' in call
Looking at the Realm documentation reveals only retrieving all persisted data--apparently without having a primary key defined--, or, alternatively, a single object, specified by the primary key.
Sifting through The Net brings up pretty much the same.
Given the model above, how can I retrieve all persisted data?
-- Sil68
EDIT
I've also defined a class facilitating this Realm model of mine, which basically carries out the following steps:
generate some random data;
persist data via the Realm model;
read data in again;
compare generated with read data.
The code
import Foundation
import RealmSwift
class DataRealmRecord {
// properties
private(set) var recDBPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
private(set) var recDBSubPath = "Persistency"
private(set) var recDBFile = "data.realm"
private(set) var recRealm: Realm?
private(set) var recRealmCfg: Realm.Configuration?
private(set) var rec = List<RealmRecord>()
private(set) var startTime = 0.0
private(set) var stopTime = 0.0
private(set) var runTime = 0.0
private(set) var outLog = ""
private(set) var realmOk = false
// methods
init() {
// assemble destination folder/database name
do {
try FileManager.default.createDirectory(atPath: recDBPath + "/" +
recDBSubPath,
withIntermediateDirectories: true,
attributes: nil)
recDBFile = recDBPath + "/" + recDBSubPath + "/" + recDBFile
realmOk = true
} catch let error as NSError {
outLog += error.localizedDescription
realmOk = false
} // do/try/catch
// configure realm database
if (realmOk) {
self.recRealmCfg = Realm.Configuration(fileURL: Foundation.URL(string: self.recDBFile))
do {
self.recRealm = try Realm(configuration: self.recRealmCfg!)
realmOk = true
} catch let error as NSError {
outLog += error.localizedDescription
realmOk = false
} // do/try/catch
} // if
} // init
// generate test data
func generateData(noRecs: Int? = 1000, simDat: SimulateData?) {
for i in 1...noRecs! {
let realmRec = RealmRecord(id: i,
text: String(format: "Record #%04d", i),
var1: simDat?.datnorm[i - 1] ?? 1.1,
var2: simDat?.datpois[i - 1] ?? 2,
var3: simDat?.datunif[i - 1] ?? 3.3,
var4: simDat?.datbern[i - 1] ?? 4,
cusr: "me")
self.rec.append(realmRec)
} // for
} // generateData
// retrieve test data from persistent storage
func loadData() -> List<RealmRecord> {
if let inrec = self.recRealm?.object(RealmRecord.self) {
return inrec
} else {
return List<RealmRecord>()
} // if/else
} // loadData
// save test data to persistent storage
func saveData() {
do {
try recRealm?.write {
recRealm?.add(self.rec, update: true)
} // try
} catch let error as NSError {
outLog += error.localizedDescription
} // do/try/catch
} // saveData
// compare two data sets
func compareData(rec1: List<RealmRecord>, rec2: List<RealmRecord>) -> Bool {
var rc = false
if rec1.count == rec2.count {
rc = true
for i in 0..<rec1.count {
rc = rc && (rec1[i] == rec2[i])
} // for
} // if
return rc
} // compareData
// run a full evaluation cycle
// (1) generate test data;
// (2) save test data to persistant storage;
// (3) retrieve test data from persistant storage;
// (4) compare generated data with retrieved data.
func fullCycle(noRecs: Int? = 1000, simDat: SimulateData?, prnData: Bool? = false) {
// start execution time measurement
self.startTime = Double(CFAbsoluteTimeGetCurrent())
// execute the full cycle
self.generateData(noRecs: noRecs, simDat: simDat) // (1)
self.saveData() // (2)
let rec2 = self.loadData() // (3)
let cmpRec = compareData(rec1: self.rec, rec2: rec2) // (4)
// stop execution time measurement & calculate elapsed time
self.stopTime = Double(CFAbsoluteTimeGetCurrent())
self.runTime = self.stopTime - self.startTime
} // fullCycle
} // DataRealmRecord
Issue at hand is, this code fails to compile due to the error message mentioned above (in method loadData()).
The Swift compiler is telling you that it thinks you're trying to call Realm.object(ofType:forPrimaryKey:), which retrieves a single object based on the value of its primary key. It sounds like you really want to call Realm.objects(_:) to retrieve all objects of a given type. Note that this returns a Results<T>, not a List<T>.

OS X API to enumerate a folder including AppleDouble files (beginning with a "._")?

The following 4 NSFileManager API fail to enumerate AppleDouble files beginning with a "._"
enumeratorAtPath,
enumeratorAtURL,
contentsOfDirectoryAtPath,
contentsOfDirectoryAtURL
Which API should be used to get them enumerated without fail?
The below does the job, just in case someone needs it
func contentsOfDirectoryUsingCAPI() -> [String]? {
var contentsOfDirectory: [String]?
if let folderPathCString = fileURL.path!.cStringUsingEncoding(NSUTF8StringEncoding) {
// Open the directory
let dir = opendir(folderPathCString)
if dir != nil {
contentsOfDirectory = []
// Use readdir to get each element
var entry = readdir(dir)
while entry != nil {
let d_namlen = entry.memory.d_namlen
let d_name = entry.memory.d_name
// dirent.d_name is defined as a tuple with
// MAXNAMLEN elements. We want to convert
// that to a null-terminated C string.
var nameBuf: [CChar] = Array()
let mirror = Mirror(reflecting: d_name)
for _ in 0..<d_namlen {
for child in mirror.children {
if let value = child.value as? CChar {
nameBuf.append(value)
}
}
}
// Null-terminate it and convert to a String
nameBuf.append(0)
if let name = String.fromCString(nameBuf) {
contentsOfDirectory?.append(name)
}
entry = readdir(dir)
}
closedir(dir)
}
}
return contentsOfDirectory
}