I have just upgraded to Swift 3 and get an error on the following code that is within a UITableViewController.
The code is return segue from a form, and is intended to refresh the table inserting the new entry from the form. This worked fine on 2.2, and I have changed NSIndexPath to IndexPath
#IBAction func saveTripFormViewList(segue:UIStoryboardSegue){
trips = uiRealm.objects(Trip.self)
let indexPath = IndexPath(forRow: trips!.count - 1, inSection: 0)
tableView.insertRowsAtIndexPaths([indexPath], withRowAnimation: .Automatic)
}
The error I get is
Argument labels '(forRow:, inSection:)' do not match any available overloads
Any advice on how to resolve the error
In Swift 3 it's just IndexPath(row:section:).
FYI, IndexPath is a new value type (a struct) that does the same job as NSIndexPath (a class). (NSIndexPath's initializer has changed as well.) Also, I believe they're toll-free bridged, but don't quote me on that.
Related
I am getting an error that I don't understand. I am not sure if it is a compiler error or if I am doing something wrong?
Inside a swiftUI View I have a list showing elements from core data (Figure 1). In the example below I replaced the t.name with "yo" for some undefined reason 😅.
Anyway, the tasks is a fetch request from Core Data:
#FetchRequest(entity: Task.entity(), sortDescriptors: []) var tasks: FetchedResults<Task>
FIGURE 1: Works fine to build and run the app.
FIGURE 2: Does not work to build and run the app.
Please help me understand what I am doing wrong or is this a compiler bug? Why can't I add the if block inside the ForEach? I can provide more information if needed. Thanks!
You can use if inside ForEach, but you should remember that ForEach is not language operator foreach but a struct type with ViewBuilder in constructor with generics in declaration, so it needs to determine type, which in your case it cannot determine.
The possible solution is to tell explicitly which type you return, as below (tested with Xcode 11.2 / iOS 13.2)
ForEach(tasks, id: \.id) { name -> Text in
if (true) {
return Text("name")
}
}
You have to return nil in case of a false condition. So you need to declare parameter as Optional and return nil in case of a false condition (XCode - 11.3.1).
ForEach(tasks, id: \.id) { t -> Text? in
return condition ? Text("text") : nil
}
}
For some reason, in the ForEach.init you're using, the view building closure isn't annotated with #ViewBuilder. This means that the if/else you're using is Swift's own if statement, not the SwiftUI construct which returns a _ConditionalContent.
I don't know if it's considered a bug by Apple, but I would definitely consider it to be one.
The easiest workaround is just to wrap the if/else in a Group - the Group.init is a #ViewBuilder, so will handle the if/else correctly.
What also worked for me was using a Group inside the ForEach like this:
ForEach(self.items, id: \.self { item in
Group {
if item.id == 0 {
Text(String(item.id))
}
}
}
I want to change title with an observable int.
in view Model
var index = Variable<Int>(0)
in view Controller
let title = ["title1","title2","title3","title4","title5"]
override func viewWillAppear(_ animated: Bool) {
self.viewModel.index.value = 0
self.viewModel.index
.asObservable()
.map( {self.periodText[$0]
})
.bind(to: self.titleLabel.rx.text)
.addDisposableTo(self.disposeBag)
}
When i do this i have an error in the blind(to) :
fatal error: unexpectedly found nil while unwrapping an Optional value
The function never pass in the .map
How i can change the title when my index change in RX Swift?
The textField on the storyBoard doesn't connect to the #IBOutlet titleLabel which
results in this error message.
My guess is your self.periodText array does not have as many elements as your index value, thus running out of bounds.
Alternatively, it could be that you're defining your diposeBag as self.disposeBag: DisposeBag!. If you've then forgotten to create an instantiation of it before viewWillAppear, it is still set to nil, in which case you'll run into this error too.
Generally, it is best to share more details on where your error occurs, so it is easier to narrow it down.
Edit: Please note the question below discusses using delegation between
2 viewcontrollers that are also implemented in a UITabBarController.
I've done a fair bit of searching here and on YouTube, but haven't seen my issue replicated elsewhere. I'll keep it to the point.
I have 2 view controllers that I coded myself -not generated by XCode-; TabOneController, and TabTwoController
below are the coding for both...
import UIKit
class TabOneController: UIViewController{
private let instanceOfTabOneView = TabOneView()
var vc1Delegate: fromOneToTwo!
override func loadView() {
super.loadView()
view.addSubview(instanceOfTabOneView.buildTheVu())
view.backgroundColor = UIColor.white
runThisOnce()
}
func runThisOnce(){
vc1Delegate.passTheValue(heroNameIs: "pass this to TabTwoController")
}
}
protocol fromOneToTwo{
func passTheValue(heroNameIs: String)
}
as for tab 2...
import UIKit
class TabTwoController: UIViewController, fromOneToTwo{
private let instanceOfTabTwoView = TabTwoView()
override func loadView() {
super.loadView()
view.addSubview(instanceOfTabTwoView.buildTheVu())
assignDelegateToSelf()
}
func assignDelegateToSelf(){
let instanceTabOne = TabOneController()
instanceTabOne.vc1Delegate = self
}
func passTheValue(heroNameIs:String){
instanceOfTabTwoView.txtFld.text = heroNameIs
}
}
I'm getting the following error at runtime -the app builds successfully-...
fatal error: unexpectedly found nil while unwrapping an Optional value
on the following line...
vc1Delegate.passTheValue(heroNameIs: "pass this to TabTwoController")
When I comment out the above line, the app builds and runs successfully, but of course the app doesn't execute the delegation.
I kinda understand what the compiler is trying to tell me, that the
vc1Delegate
hasn't been instantiated -I guess-. But I searched under every rock, and can't seem to find how to get around this.
I'd appreciate any help or guidance. Sorry if my code seems immature, I'm new to Swift and programming in general. Thank you.
In a UITabBarController, the first tab is instantiating by default. The view controller initialization executes the loadView and finds a nil because the second tab did not initialize yet; this is normal behavior. My suggestion is making the delegate weak optional with the ? suffix and run the delegate code elsewhere. Also, always capitalize the first letter in class and protocol names.
weak var vc1Delegate: FromOneToTwo?
If this structure is mandatory, try with a custom notification observer instead.
First thing first, your error happens in line var vc1Delegate: fromOneToTwo! while you declared this delegate variable as not null but then calling passTheValue on it. A correct practice will be
var vc1Delegate: fromOneToTwo?
func runThisOnce(){
if let delegate = vc1Delegate {
vc1Delegate.passTheValue(heroNameIs: "pass this to TabTwoController")
}
}
Secondly, you are not using delegate correctly. In the assignDelegateToSelf() function, you are creating a new instance of TabOneController
and then assign delegate. Instead, you need to find out the existing TabOneController instance and assign delegate.
I try this and worked add delegate = self in table cellforRowAt
like this
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "ChapterPracticeTableViewCell") as! ChapterPracticeTableViewCell
cell.deligate = self
return cell
}
I have a variable called service, like so:
var service: AnyObject = [] //Swift 2.3
My question is how to migrate this to Swift 3. I'm a noob in iOS, I've looked in Internet but could't get it.
PS. I get an error "Contextual type 'AnyObject' cannot be used with array literal"
Thanks in advance!
What you were doing was always wrong. If you have no value to supply at initialization time, use an Optional. Ideally you should declare this as the actual type of value that it will be when gets a value (rather than a catch-all type such as AnyObject). But if you can't do that, then just use Any?:
var service : Any?
Or, if this thing's type is known — for example, if you know it's going to be a Dictionary — then declare it as a Dictionary, possibly by supplying an empty Dictionary, like this:
var service = [AnyHashable:Any]()
var service = [AnyObject]() will work.
Here is an example of using it in Swift -3
var service = [AnyObject]() //Swift 3
service = ["a" as AnyObject,"b" as AnyObject]
print(service)
Output:
a,b
It seems with the release of iOS 10, a few things have broken. The major one for me has been the use of NSMutableDictionary and NSMutableArray. Both no longer seem to be able to parse a string of JSON and instead give out a nil while in pre iOS 10 they populated as expected. The only way around this I've found is to use NSDictionary and NSArray respectively and then use the init methods to cast back. For example:
let json = "{ \"code\": \"abcde\", \"name\": \"JP Morgan\" }"
json as! NSMutableDictionary // gives nil
NSMutableDictionary(dictionary: json as! NSDictionary) // works :)
let json = "[{ \"code\": \"abcde\", \"name\": \"JP Morgan\" }]"
json as! NSMutableArray // gives nil
NSMutableArray(array: json as! NSArray) // works :)
I would like to know why?
And I hope this helps someone solve their issue...
The Foundation types NSMutableArray / NSMutableDictionary are not related to the Swift counterparts and cannot be bridged / coerced from a literally created Swift type. But that's not new in Swift 3.
Basically do not use NSMutableArray / NSMutableDictionary in Swift unless you have absolutely no choice for example interacting with a few low level CoreFoundation API. The native Array / Dictionary types used with var provide the same functionality (except value vs. reference semantics) and in addition the types of the containing objects.