This question already has answers here:
Different process between Struct and Class in mutating asynchronously in Swift3
(2 answers)
Closed last year.
I have the following code I'm using with SwiftUI:
import Foundation
public struct Trigger {
public var value = false
public mutating func toggle() {
value = true
let responseDate = Date().advanced(by: 3)
OperationQueue.main.schedule(after: .init(responseDate)) {
moveBack()
}
}
private mutating func moveBack() {
value = false
}
}
However, I'm getting an error:
Escaping closure captures mutating 'self' parameter
I understand that changing the struct to a class would solve this issue, but is there any way to actually capture a mutating self in an escaping closure in a struct?
As you have found, the quick solution is to use a reference type, a class. But why is this the case?
Swift structs are value types, so they are immutable. You can mark a function as mutating to indicate to the compiler that a function mutates the struct, but what does that actually mean?
Consider a simple struct:
struct Counter {
var count
init(_ count: Int = 0)
{
self.count = count
}
mutating func increment() {
self.count+=1
}
}
Now, try and assign an instance of this to a let constant:
let someCounter = Counter()
someCounter.increment()
print(someCounter.count)
You will get an error; you need to use a var.
var someCounter = Counter()
someCounter.increment()
print(someCounter.count)
What actually happens when you call a mutating func is that a new Counter is created, with the new count and it is assigned to someCounter. It is effectively saying someCounter = Counter(someCounter.count+1)
Now, think what would happen if you could mutate self in an escaping closure - That new Counter is going to be created at some unspecified time in the future, but execution has already moved on. It is too late to update someCounter.
The other advantage of using a class, as you have found, is that you can use ObservableObject, which makes updating your SwiftUI views much easier.
Solution I finished with:
import Foundation
import Combine
public final class IntervalTrigger: ObservableObject {
private let timeInterval: TimeInterval
#Published var value = false
public init(_ timeInterval: TimeInterval) {
self.timeInterval = timeInterval
}
public func toggle() {
value = true
let responseDate = Date().advanced(by: timeInterval)
OperationQueue.main.schedule(after: .init(responseDate)) { [weak self] in
self?.value = false
}
}
}
Related
My goal is to use a button (that contains multiple messages) to trigger a text (making a marker such as first click will be method 1, second click will be method 2) correspondingly added at the end of the my data (after joined(separator: "~")) so that it could help me to analyze which button was clicked when I look back at the data.
Currently, I have a struct that will output the data:
struct CaptureData {
var vertices: [SIMD3<Float>] //A vector of three scalar values. It will return a list of [SIMD3<Float>(x,y,z)]
var mode: Mode = .one
mutating func nextCase() { // the data method will be changed
mode = mode.next()
}
var verticesFormatted : String { //I formatted in such a way so that it can be read more clearly without SIMD3
let v = "<" + vertices.map{ "\($0.x):\($0.y):\($0.z)" }.joined(separator: "~") + "trial: \(mode.next().rawValue)"
return "\(v)"
}
}
Based on #Joshua suggestion
enum Mode: String, CaseIterable {
case one, two, three
}
extension CaseIterable where Self: Equatable {
var allCases: AllCases { Self.allCases }
var nextCase: Self {
let index = allCases.index(after: allCases.firstIndex(of: self)!)
guard index != allCases.endIndex else { return allCases.first! }
return allCases[index]
}
#discardableResult
func next() -> Self {
return self.nextCase
}
}
And the button is alternating the messages after each click,
var x = 0
var instance = CaptureData(vertices: [SIMD3<Float>])
// Next button for changing methods
#IBAction func ChangingTapped(_ btn: UIButton) {
if(x==0){
Textfield.text = "changing to driving"
}
else if(x==1){
Textfield.text = "changing to walking"
instance.nextCase()
}
else{
Textfield.text = "changing to cycling"
instance.nextCase()
}
x += 1
}
Updates: I am able to print one of the methods , .two (method two), after separator: "~". However, at the moment I am still not be able to click button to switch the case in the data.
The main problem is the initialization of variables. I am not able to define var instance = CaptureData(vertices: [SIMD3<Float>]) because it comes with error: Cannot convert value of type '[SIMD3<Float>].Type' to expected argument type '[SIMD3<Float>]'
I am sorry if my explanation is a bit messy here. I am trying to describe the problem I have here. Let me know if there is anything missing! Thank you so much in advance.
Enums is a data type that is more like a constant but much more readable.
An example will be passing in a status to a function.
enum Status {
case success
case failure
}
func updateStatus(_ status: Status) {
statusProperty = status
}
// somewhere in your code
instance.updateStatus(.success)
versus using an Int as a value.
func updateStatus(_ status: Int) {
statusProperty = status
}
// somewhere in your code
instance.updateStatus(1) // eventually you'll forget what this and you'll declare more of a global variable acting as constant, which technically what enums are for.
Enums in swift are a bit different though, much more powerful. More info about enums here
Back to the topic.
enum Mode: String, CaseIterable {
case one, two, three
}
extension CaseIterable where Self: Equatable {
var allCases: AllCases { Self.allCases }
var nextCase: Self {
let index = allCases.index(after: allCases.firstIndex(of: self)!)
guard index != allCases.endIndex else { return allCases.first! }
return allCases[index]
}
#discardableResult
func next() -> Self { // you don't need to update self here, remember self here is one of the items in the enum, i.e. one, so assigning one = two just doesn't work.
return self.nextCase
}
}
// The data struct
struct CaptureData {
var mode: Mode = .one
// we add a mutation function here so we can update the mode
mutating func nextCase() { // the data your concern about, that can actually mutate is the mode property inside CaptureData struct.
mode = mode.next()
}
}
So lets say somewhere in the app you can use it like this you initialised an instance of CaptureData:
var instance = CaptureData() // Don't forget it should be var and not let, as we are updating its property.
instance.nextCase() // get the next case, initially it was .one
print(instance.mode) // prints two
instance.nextCase() // from .two, changes to .three
print(instance.mode) // prints three
Hope this helps.
How do I ensure that a given template parameter is a protocol?
A GKEntity has a function called component(ofType: class) and i want to add component(ofProtocol: Protocol). It does look like this:
extension GKEntity {
func component<T: Protocol>(ofProtocol: T) -> T? {
return self.components.first() { component in
return component.conforms(to: ofProtocol)
} as? T
}
}
I want to use it in an component which holds a reference to the entity like this:
let component = self.entity?.component(ofProtocol: SpriteComponentProtocol)
but somehow i always get:
Showing All Messages
Cannot convert value of type 'SpriteComponentProtocol.Protocol' to expected argument type 'Protocol'
Update:
The idea is that i have a component for a Sprite:
protocol SpriteComponentProtocol {
var spriteNode: SKSpriteNode { get set }
}
class SpriteComponent: GKComponent {
var spriteNode: SKSpriteNode?
}
And a other component for the control:
protocol PlayerControlComponentProtocol {
var steerAngle: Double { get set }
}
class PlayerControlComponent: GKComponent, PlayerControlComponentProtocol {
var steerAngle: Double = 90.0
override func update(deltaTime seconds: TimeInterval) {
//here i do manipulate the spriteComponent.spriteNode
let comp = self.entity?.component(ofProtocol: SpriteComponentProtocol)
}
}
I want to be able to exchange the SpriteComponent at any time.
The problem with your code is that Protocol is an opaque type that describes an Obj-C protocol, so if you want to bridge SpriteComponentProtocol.self over to it, you need to mark SpriteComponentProtocol as #objc (but even if you did; you wouldn't be able to cast to T, because the returned instance isn't of type Protocol).
But that being said, you don't need to use the Obj-C Protocol type or conforms(to:) method here, you can simply use the conditional type-casting operator as? in an overload of component(ofType:) without the GKComponent constraint on T:
extension GKEntity {
func component<T>(ofType type: T.Type) -> T? {
return self.components.lazy.flatMap{ $0 as? T }.first
}
}
We're using lazy here in order to avoid evaluating all the components, and then flatMap(_:) and first in order to get the first element that's castable to T (and in the case of T being a protocol type, this gives us the first element that conforms to the protocol).
You can then simply use it like so:
protocol SpriteComponentProtocol {
var spriteNode: SKSpriteNode { get set }
}
class PlayerControlComponent: GKComponent {
override func update(deltaTime seconds: TimeInterval) {
let comp = self.entity?.component(ofType: SpriteComponentProtocol.self)
}
}
And in Swift 4, you can remove this overload entirely, and instead simply call GKEntity's component(ofType:) method with a class existential metatype:
let comp = self.entity?.component(ofType: (GKComponent & SpriteComponentProtocol).self)
As now T satisfies the : GKComponent constraint. You can then access both GKComponent methods and SpriteComponentProtocol protocol requirements on the unwrapped instance.
So, I've been working on a Weather App with the following brief Data Model
class CurrentWeather
{
private var _cityName: String!
private var _date: String!
private var _weatherType: String!
private var _currentTemp: Double!
var cityName: String
{
if _cityName == nil
{
_cityName = ""
}
return _cityName
}
// Same idea for getters var date, var weatherType and
// var currentTemp (returns 0.0 if it is nil)
// Not showing that here
func downloadWeatherDetails(completed: DownloadComplete)
{
// Function which computes values though a url and stores in instance variables
// Not showing the entire actual function here
self._cityName = name.capitalized. // value computed earlier
print(self._cityName)
self._weatherType = main.capitalized // value computed earlier
print(self._weatherType)
self._currentTemp = currentTemp - 273.15 // value computed earlier
print(self._currentTemp)
completed()
}
}
where the type DownloadComplete is a type alias to ()->()
In the main ViewController.swift, I have created an object and called this function (with trailing closure syntax)
var currentWeather: CurrentWeather!
override func viewDidLoad()
{
super.viewDidLoad()
currentWeather = CurrentWeather()
currentWeather.downloadWeatherDetails {
self.updateMainUI() // I have created this function
}
}
func updateMainUI()
{
dateLabel.text = currentWeather.date
currentTempLabel.text = String(currentWeather.currentTemp)
locationLabel.text = currentWeather.cityName
currentWeatherTypeLabel.text = currentWeather.weatherType
currentWeatherImage.image = UIImage(named: currentWeather.weatherType)
print("Tested: \(currentWeather.currentTemp)")
print("Tested: \(currentWeather.cityName)")
print("Tested: \(currentWeather.weatherType)")
}
So the Expected output:
Logically,
I have created a CurrentWeather object
Called the downloadWeatherDetails function which should load the different computed values in the private vars.
Call the user defined updateMainUI function which displays the different values on my app's UI
So the output should be like
Birim. //cityname
Clear. //weatherType
29.134 //currentTemp
Tested: 29.134
Tested: Birim
Tested: Clear
But the output which I get is
Tested: 0.0
Tested: (indicating "")
Tested: (indicating "")
Birim
Clear
29.134
So, basically the functions downloadWeatherDetails and updateMainUI are called in the wrong order? Why is this so? Is this somehow related to asynchronous execution of functions?
I have tried not using the trailing closure, but it still doesn't work.
I also tried leaving the closure empty and calling updateMainUI after downloadWeatherDetails call like this
currentWeather.downloadWeatherDetails {
}
self.updateMainUI()
But this too doesn't work. Any ideas of why the functions are called in the wrong order?
UPDATE:
the underscore variables are private vars while the non-underscore variables are the getters like
var cityName: String
{
if _cityName == nil
{
_cityName = ""
}
return _cityName
}
// Same idea for getters var date, var weatherType and
// var currentTemp (returns 0.0 if it is nil)
// Not showing that here
UPDATE 2:
Project files are here(in case one may want to refer): https://github.com/danny311296/Weather-App
You must call your 'completed()' function within the Alamofire request callback. Since the request function is asynchronous it does not wait for it to finish before executing completed().
Alamofire.request(CURRENT_WEATHER_URL).responseJSON { response in
// handle response...
// when done call completed
completed()
}
I guess the issue is that your "updateMainUI" method is being called before the completion of download process. Although you have implemented the Completion Listener, this should be working but I don't what's wrong with that. Try to use some other methods like delegation or notification to observe the downloading processes.
Check this link to see other method to observe completion:
https://medium.com/ios-os-x-development/ios-three-ways-to-pass-data-from-model-to-controller-b47cc72a4336
In Swift I have this Singleton
struct Networking {
static let shared = Networking()
private var observed: Set<String> = []
}
I have to manipulate observed and I need to create useful method to insert and remove member in Set.
mutating func addObserver(for member: String) {
//other code
observed.insert(member)
}
mutating func removeObserver(for member: String) {
//other code
observed.remove(member)
}
The problem is when I try to call this methods like this
Networking.shared.addObserver(for: "x")
because I'm getting this error
cannot use mutating on immutable value: “shared” is a “let” constant
This error is pretty clear. shared is let and obviously it cannot be modified. But to modify the var I need to declare method as mutating. It's a vicious circle.
Any ideas?
Thanks.
If you want your Networking object to act as a singleton, why not make it a class instead of a struct?
class Networking {
static let shared = Networking()
private var observed: Set<String> = []
func addObserver(for member: String) {
//other code
observed.insert(member)
}
func removeObserver(for member: String) {
//other code
observed.remove(member)
}
}
Networking.shared.addObserver(for: "x")
This simplifies the code and solves your issue.
Basically your syntax is wrong, Networking() creates a new instance of the class.
To use the struct as singleton you have to write
Networking.shared.addObserver(for: "x")
Then declare shared as mutable
static var shared = Networking()
There is also another way of doing it
class Networking {
static let shared = Networking()
var observed: Set<String> = [] {
didSet {
print("set has changed")
}
}
}
Value Type
Since Set is a struct (a value type), the didSet block will be executed every time you add or remove and element to Set.
Networking.shared.observed.insert("a")
set has changed
Networking.shared.observed.insert("b")
set has changed
Networking.shared.observed.remove("a")
set has changed
What is the best practice to check if an array of objects has been loaded in Swift?
Say, if I declare an array in a class, and it is lazily loaded. Apple's docs say the array declaration / initialization is something like
var events = [Event]()
I suppose the above means the array is already initialized (ie. not nil).
Then, I need a function like:
func getEvents() -> [Event] {
// check if array is nil, and if so, load the events (not: which could be 0 events)
return events
}
In Java, I would declare something like
ArrayList<Event> events;
public ArrayList<Event> getEvents() {
if(!events) { // null means never been loaded
events = new ArrayList<Event>();
events = loadEvents(); // load the events, which could be zero
}
}
What is the best practice to code the equivalent in Swift?
Lazy Stored Property
In Swift you can declare a lazy stored property: it does exactly what you need.
struct Event { }
class Foo {
lazy var events: [Event] = { self.loadEvents() }()
private func loadEvents() -> [Event] {
print("Loading events")
return [Event(), Event(), Event()]
}
}
We associated a closure to the events property. The closure defines how the events property should be populated and will be executed only when the property is used.
let foo = Foo()
print(foo.events) // now the events property is populated
You may experience an Array in these ways:
var vect = [String]()
if vect.isEmpty {
print ("true")
}else{
print("false")
}
Or
func ceck()->Void{
guard !vect.isEmpty else{
print ("true")
return // return, break, or another logic this is example
}
}