Connected Objective-C classes to the project Swift, as it is written here:
https://developer.apple.com/library/prerelease/ios/documentation/swift/conceptual/buildingcocoaapps/MixandMatch.html
There's this code of the test project:
import UIKit
class ViewController: UITableViewController {
var locations:NSArray=[]
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var jsonLoader:JSONLoader
var url = NSURL(fileURLWithPath: "http://mechnikova.info/api/pic2.php?task=1")
dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_HIGH, 0), {
self.locations = jsonLoader.locationsFromJSONFile(url)
})
}
JSONLoader - Objective-C class. This class is connected normally, without errors.
Get an error indicating that the character "{" in the function dispatch_async:
Variable 'jsonLoader' used before initialized
Something is wrong in declaration variables.... Help please!
You can use former methods such as objectForKey() or access a specific element in an array using square brackets. Examples can be found at this link. Hope this will help you parse your json!
I fixed it:
var jsonLoader:JSONLoader = JSONLoadeer()
:-)
Related
I got a ListView in SwiftUI and want to generate RowViews depending on a given property.
Therefore I want to check the property of the element in the ForEach loop.
Xcode does not recognize the type of the element, thats why i want to cast the element to the correct Class which is an NSManagedObject Subclass.
I created the NSManagedObject Subclass from the xcdatamodeld (Editor -> Create NSManagedObject Subclass...).
The NSManagedObject Subclass is created in objective-c and I added the .h file to the bridging header.
I have no problem working with the NSManagedObject Subclass in the rest of the project.
I can even create an property of that Type in the same Struct like so:
struct MyListView: View {
var test : MyNSManagedObjectSubclass //Xcode does recognize it here
For some reason Xcode wont recognize the NSManagedObject Subclass inside the ForEach Loop.
code looks like:
struct MyListView: View {
var test : MyNSManagedObjectSubclass //Xcode does recognize it here
#EnvironmentObject private var fetchResultsContainer : FetchedResultsContainer
var body: some View {
NavigationView{
VStack{
ForEach(fetchResultsContainer.results , id: \.identifier) { result in
if let castedResult = result as! MyNSManagedObjectSubclass { //Xcode does not recognize it here
if castedResult.property{
ResultRowView(input: result)
}
}
}
}
}
}
}
FetchedResultsContainer:
#objc class FetchedResultsContainer : NSObject, ObservableObject{
#objc #Published var results: [MyNSManagedObjectSubclass]()
#objc static let sharedInstance: FetchedResultsContainer = {
let instance = FetchedResultsContainer()
return instance
}()
}
I feel like im missing something obvious, as im still quite new to Swift and SwiftUI.
Appreciate your help.
Ok after taking a step back and simply coding it again from scratch it simply worked an no casting was needed...
List(fetchResultsContainer.results, id: \.identifier) { result in
if (result.property == 0) {
//do something
}
}
My assumption that the casting was not working was not correct.
I probably checked result.property == false, with property being of type NSNumber and it gave out some strange compile errors that led me on the wrong path.
I am looking at the Swift code of the ThemeKit theming library.
In particular I would like to understand the following code in NSColor+ThemeKit.swift:
// ThemeKit.set() replacement to use theme-aware color
#objc public func themeKitSet() {
// call original .set() function
themeKitSet()
// check if the user provides an alternative color
if ThemeManager.shared.isEnabled && isThemeOverriden {
// call ThemeColor.set() function
ThemeColor.color(with: Selector(colorNameComponent)).set()
}
}
There is what appears to be an endless recursive call, but presumably can't be, since the code works fine. This is confirmed by setting a breakpoint on the call to themeKitSet(). It is not possible to step into the call and execution continues without recursion.
Earlier in the file there is the following call:
swizzleInstanceMethod(cls: NSClassFromString("NSDynamicSystemColor"), selector: #selector(set), withSelector: #selector(themeKitSet))
With the implementation in NSObject+ThemeKit.swift as follows:
/// Swizzle instance methods.
#objc internal class func swizzleInstanceMethod(cls: AnyClass?, selector originalSelector: Selector, withSelector swizzledSelector: Selector) {
guard cls != nil else {
print("Unable to swizzle \(originalSelector): dynamic system color override will not be available.")
return
}
// methods
let originalMethod = class_getInstanceMethod(cls, originalSelector)
let swizzledMethod = class_getInstanceMethod(cls, swizzledSelector)
// add new method
let didAddMethod = class_addMethod(cls, originalSelector, method_getImplementation(swizzledMethod!), method_getTypeEncoding(swizzledMethod!))
// switch implementations
if didAddMethod {
class_replaceMethod(cls, swizzledSelector, method_getImplementation(originalMethod!), method_getTypeEncoding(originalMethod!))
} else {
method_exchangeImplementations(originalMethod!, swizzledMethod!)
}
}
I suspect this is responsible for the magic, but my limited understanding of both Swift and Objective-C is letting me down.
What is happening here? Why is the apparently recursive call not actually recursive?
You correctly identified the magic bit: it's called method swizzling, and it's a way of wholesale replacing an existing method implementation.
You'll see this seemingly-recursive pattern a lot when method swizzling: that themeKitSet call actually runs the original implementation, as the comment says. It's because swizzling swaps the implementations of two methods, in this case themeKitSet and NSDynamicSystemColor.set.
Therefore, post-swizzle, NSDynamicSystemColor.set runs the code you see there, and themeKitSet has become the original implementation.
I got some Objective-C files imported in Swift project, and tried to access some Swift classes.
According to Apple's Guideline, I added the #objc to the method I wanted it to be exposed to Objective-C files.
But the question is, does this "#objc" have any side effect to my Swift project?
The following code is a singleton local data manager.
#objc class LocalDataManager {
#objc public static let shared = LocalDataManager()
private init() {}
#objc var nickName: String {
get { return loadData("nickName") } // loadData is a convenience access method to UserDefaults
set { UserDefaults.standard.set(newValue, forKey: "nickName") }
}
}
The #objc keyword does severely affect performance. The Apple docs states:
applying the #objc attribute can increase the compiled size of an app and adversely affect performance.
Therefore, only use #objc if you really need too.
I'm getting an error stating: Property 'clientChatLoad' not found on object of type 'DataManager' when using the following implementation:
AppDelegate.m
#import "IOS_Project_Name-Swift.h"
#class DataManager;
...
DataManager.clientChatLoad(0){ data, error in
guard data != nil else {
print(error)
return
}
guard let data = data else { return }
let json = JSON(data: data)
if let result = json["success"].bool{
if (result){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(0.5 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) { () -> Void in
NSNotificationCenter.defaultCenter().postNotificationName("refreshChatDetails", object: nil, userInfo:nil)
}
}
}
DataManager.swift
...
#objc class func clientChatLoad(_ chatId: Int, completionHandler: #escaping (Data?, NSError?) -> ()) -> URLSessionTask {
// AJ TO-DO
var defaults = UserDefaults.standard
let URL = Foundation.URL(string: chatUrl)!
var request = URLRequest(url: URL, cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, timeoutInterval: 3.0)
request.httpMethod = "POST"
...
From what I understand adding #class DataManager to my Objective C class as well as #objc before class func clientChatLoad should expose the Swift code to my Objective C class... but I still receive an error. What might I have overlooked?
At first I thought your code was just some sort of misprint, but over the course of discussion in the comments I have come to realize that it is real and that you are attempting to put Swift code into an Objective-C file (namely AppDelegate.m). You cannot do that! An Objective-C file must be written entirely in Objective-C!
I would question whether you really even need a hybrid app (an app written in two languages). Your life will be much simpler if you pick just one language and write the whole app in that language. But if you are going to have a hybrid app, you need to learn Objective-C and write the Objective-C in Objective-C.
make sure your DataManager class inherits from NSObject. You should be good.
How do I fix this compilation error?
dynamic var users = [User]()
Property cannot be marked dynamic because its type cannot be represented in Objective-C
I need dynamic so that certain view controllers can observe (via KVO) users and update their views when users changes.
Just as the error mentions
error, not #objc : NSObject:
class A{
}
func something(){
dynamic var a = [A]()
}
should be changed to:
#objc class A:NSObject{
}
func something(){
dynamic var a = [A]() //works, all good
}