I want to switch between 2 image-sources.
I have the following struct:
struct AppConstants {
static var imageSource:String = "goral"
static var fixURL = URL(string: "https://picsum.photos/375/375/?random")
struct Domains {
static let baseurl = "http://www.m.myapp2go.de/services/"
}...
In the following code in my viewController and i can not access the var "AppConstants.fixURL" in the switch statement:
switch AppConstants.imageSource { // is filled with a value of a picker
case "goral":
let randomInt = Int.random(in: 1...12)
let AppConstants.fixURL = URL(string: "http://www.m.myapp2go.de/pics/\(randomInt)_goral_item.jpg")
case "picsum":
let randomInt = Int.random(in: 1...1080)
let AppConstants.fixURL = URL(string: "https://picsum.photos/375/375/?\(randomInt)")
default:
print("other")
}
With the error-message:
Reference to member 'fixURL' cannot be resolved without a contextual type
and
Type annotation missing in pattern
Related
I want to dynamically deliver content and display hyperlinks, but it can’t be delivered dynamically and doesn’t work
let linkTitle = "Apple Link"
let linkURL = "http://www.apple.com"
let string = "[Apple Link](http://www.apple.com)"
Text(string) // Not working
Text("[Apple Link](http://www.apple.com)") // Working
Text("[\(linkTitle)](http://www.apple.com)") // Working
Text("[\(linkTitle)](\(linkURL))") // Not working
Short Answer
Wrap the string in AttributedString(markdown: my_string_here):
let string: String = "[Apple Link](http://www.apple.com)"
Text(try! AttributedString(markdown: string))
Extension
extension String {
func toMarkdown() -> AttributedString {
do {
return try AttributedString(markdown: self)
} catch {
print("Error parsing Markdown for string \(self): \(error)")
return AttributedString(self)
}
}
}
Long Answer
SwiftUI Text has multiple initializers.
For String:
init<S>(_ content: S) where S : StringProtocol
For AttributedString:
init(_ attributedContent: AttributedString)
When you declare a static string, Swift is able to guess whether the intent is to use a String or AttributedString (Markdown). However, when you use a dynamic string, Swift needs help in figuring out your intent.
As a result, with a dynamic string, you have to explicitly convert your String into an AttributedString:
try! AttributedString(markdown: string)
you can try this taken from: How to show HTML or Markdown in a SwiftUI Text? halfway down the page.
extension String {
func markdownToAttributed() -> AttributedString {
do {
return try AttributedString(markdown: self) /// convert to AttributedString
} catch {
return AttributedString("Error parsing markdown: \(error)")
}
}
}
struct ContentView: View {
let linkTitle = "Apple Link"
let linkURL = "https://www.apple.com"
let string = "[Apple Link](https://www.apple.com)"
#State var textWithMarkdown = "[Apple Link](https://www.apple.com)"
var body: some View {
VStack {
Text(textWithMarkdown.markdownToAttributed()) // <-- this works
Text(string) // Not working
Text("[Apple Link](http://www.apple.com)") // Working
Text("[\(linkTitle)](http://www.apple.com)") // Working
Text("[\(linkTitle)](\(linkURL))") // Not working
}
}
}
Add another .init in text.
struct ContentView: View {
let linkTitle = "Apple Link"
let linkURL = "http://www.apple.com"
let string = "[Apple Link](http://www.apple.com)"
var body: some View {
Text(.init(string)) // <== Here!
Text("[Apple Link](http://www.apple.com)") // Working
Text("[\(linkTitle)](http://www.apple.com)") // Working
Text(.init("[\(linkTitle)](\(linkURL))")) // <== Here!
}
}
I had a function that cleared all objects from given entity, in swift 2:
private static func clearTable(tableName : String)
{
let appDel = UIApplication.sharedApplication().delegate as! AppDelegate
let context : NSManagedObjectContext = appDel.managedObjectContext
let request = appDel.persistentStoreCoordinator
let fetchRequest = NSFetchRequest(entityName: tableName)
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
do {
try request.executeRequest(deleteRequest, withContext: context)
} catch let error as NSError {
debugPrint(error)
}
}
recently i migrated to swift 3 and now it looks like this:
static func clearTable(_ tableName : String)
{
let appDel = UIApplication.shared.delegate as! AppDelegate
//let context : NSManagedObjectContext = appDel.managedObjectContext
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let request = NSFetchRequest(entityName: tableName)
let fetchRequest: NSFetchRequest = NSFetchRequest(entityName: tableName)
let deleteRequest = NSBatchDeleteRequest(fetchRequest: NSFetchRequest(entityName: tableName))
do {
try request.execute(deleteRequest, with: context)
} catch let error as NSError {
debugPrint(error)
}
}
As i understood, now i have to declare request and fetchRequest like
let fetchRequest: NSFetchRequest<SomeEntity> = NSFetchRequest(entityName: "SomeEntity")
The problem is that i don't know the entity beforehand. Is there any workaround or reflexion in swift 3? And i'm new to swift and core data, is this the normal way to fetch or delete objects?
All result types in Core Data including NSManagedObject conform to NSFetchRequestResult so use that as type
static func clearTable(_ tableName : String)
{
let appDel = UIApplication.shared.delegate as! AppDelegate
let context = appDel.persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: tableName)
let deleteRequest = NSBatchDeleteRequest(fetchRequest: request)
let persistentStoreCoordinator = context.persistentStoreCoordinator!
do {
try persistentStoreCoordinator.execute(deleteRequest, with: context)
} catch let error as NSError {
debugPrint(error)
}
}
This is the recommended way to delete all items of an entity.
However the deployment target must be ≥ iOS 9 / macOS 10.11.
PS: In Swift 3 I'd declare the function
static func clear(table tableName : String) { ...
Checkout this batch delete example for a better way.
I use the CodeDataStack pattern for separation of concern to make the code even more clean.
You can use extension to make your code more dynamic. Instead of passing the table name, you can simply invoke model.delete() or Model.deleteAll(). This is a common pattern used in other web frameworks.
ModelExtension.swift
extension NSManagedObject {
static func context() -> NSManagedObjectContext {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
return appDelegate.coreData.context()
}
public func delete() {
managedObjectContext?.delete(self)
}
public static func deleteAll() {
let batchDeleteRequest = NSBatchDeleteRequest(fetchRequest: self.fetchRequest())
do {
try context().execute(batchDeleteRequest)
} catch {
// Error Handling
}
}
}
Unit Test:
// My entity name is `Location`
func test_deleteAll() {
_ = fixture.buildTestLocation()
_ = fixture.buildTestLocation()
coreData.saveContext()
Location.deleteAll()
XCTAssertEqual(0, Location.all()?.count)
}
func test_delete() {
let location = fixture.createTestLocation()
XCTAssertEqual(1, Location.all()?.count)
location.delete()
coreData.saveContext()
XCTAssertEqual(0, Location.all()?.count)
}
Note that Location.all() is a custom method.
public IEnumerable<CustomBo> FindBy(Expression<Func<CustomBo, bool>> predicate)
{
Mapper.CreateMap<Expression<Func<CustomBo, bool>>, Expression<Func<Entity, bool>>>();
var newPredicate = Mapper.Map<Expression<Func<Entity, bool>>>(predicate);
IQueryable<Entity> query = dbSet.Where(newPredicate);
Mapper.CreateMap<Entity,CustomBo>();
var searchResult = Mapper.Map<List<CustomBo>>(query);
return searchResult;
}
I want to map customBo type to Entity Type..
Here customBo is my model and Entity is Database entity from edmx.
I'm using AutoMapper.
I'm Getting following Error
Could not find type map from destination type Data.Customer to source type Model.CustomerBO. Use CreateMap to create a map from the source to destination types.
Could not find type map from destination type Data.Customer to source type Model.CustomerBO. Use CreateMap to create a map from the source to destination types.
Any Suggession what I'm missiong here..
Thanks
I find a work around. I create my custom methods to map Expression.
public static class MappingHelper
{
public static Expression<Func<TTo, bool>> ConvertExpression<TFrom, TTo>(this Expression<Func<TFrom, bool>> expr)
{
Dictionary<Expression, Expression> substitutues = new Dictionary<Expression, Expression>();
var oldParam = expr.Parameters[0];
var newParam = Expression.Parameter(typeof(TTo), oldParam.Name);
substitutues.Add(oldParam, newParam);
Expression body = ConvertNode(expr.Body, substitutues);
return Expression.Lambda<Func<TTo, bool>>(body, newParam);
}
static Expression ConvertNode(Expression node, IDictionary<Expression, Expression> subst)
{
if (node == null) return null;
if (subst.ContainsKey(node)) return subst[node];
switch (node.NodeType)
{
case ExpressionType.Constant:
return node;
case ExpressionType.MemberAccess:
{
var me = (MemberExpression)node;
var newNode = ConvertNode(me.Expression, subst);
MemberInfo info = null;
foreach (MemberInfo mi in newNode.Type.GetMembers())
{
if (mi.MemberType == MemberTypes.Property)
{
if (mi.Name.ToLower().Contains(me.Member.Name.ToLower()))
{
info = mi;
break;
}
}
}
return Expression.MakeMemberAccess(newNode, info);
}
case ExpressionType.AndAlso:
case ExpressionType.OrElse:
case ExpressionType.LessThan:
case ExpressionType.LessThanOrEqual:
case ExpressionType.GreaterThan:
case ExpressionType.GreaterThanOrEqual:
case ExpressionType.Equal: /* will probably work for a range of common binary-expressions */
{
var be = (BinaryExpression)node;
return Expression.MakeBinary(be.NodeType, ConvertNode(be.Left, subst), ConvertNode(be.Right, subst), be.IsLiftedToNull, be.Method);
}
default:
throw new NotSupportedException(node.NodeType.ToString());
}
}
}
Now I'm calling it like
public CustomBo FindBy(Expression<Func<CustomBo, bool>> predicateId)
{
var newPredicate = predicateId.ConvertExpression<CustomBo, Entity>();
}
Still if anyone know how to do it by automapper then plz let me know.
Thanks
Looks like this was added after your asked your question: Expression Translation (UseAsDataSource)
Now all you have to do is dbSet.UseAsDataSource().For<CustomBo>().Where(expression).ToList();. Much nicer!
I am confused on how I can have two keys as strings and one works and the other doesn't. Error occurs in the line near the end:
println("Here's a (car.year) (car.make) (car.model)")
What is it about the "make" variable that could be causing the problem?
protocol NSCoding {
}
class Car:NSObject {
var year: Int = 0
var make: String = ""
var model: String = ""
override init() {
super.init()
}
func encodeWithCoder(aCoder: NSCoder!) {
aCoder.encodeInteger(year, forKey:"year")
aCoder.encodeObject(make, forKey:"make")
aCoder.encodeObject(model, forKey:"model")
}
init(coder aDecoder: NSCoder!) {
super.init()
year = aDecoder.decodeIntegerForKey("year")
make = aDecoder.decodeObjectForKey("make") as String
model = aDecoder.decodeObjectForKey("model") as String
}
}
class CarData {
func archiveData () {
var documentDirectories:NSArray
var documentDirectory:String
var path:String
var unarchivedCars:NSArray
var allCars:NSArray
// Create a filepath for archiving.
documentDirectories = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
// Get document directory from that list
documentDirectory = documentDirectories.objectAtIndex(0) as String
// append with the .archive file name
path = documentDirectory.stringByAppendingPathComponent("swift_archiver_demo.archive")
var car1:Car! = Car()
var car2:Car! = Car()
var car3:Car! = Car()
car1.year = 1957
car1.make = "Chevrolet"
car1.model = "Bel Air"
car2.year = 1964
car2.make = "Dodge"
car2.model = "Polara"
car3.year = 1972
car3.make = "Plymouth"
car3.model = "Fury"
allCars = [car1, car2, car3]
// The 'archiveRootObject:toFile' returns a bool indicating
// whether or not the operation was successful. We can use that to log a message.
if NSKeyedArchiver.archiveRootObject(allCars, toFile: path) {
println("Success writing to file!")
} else {
println("Unable to write to file!")
}
// Now lets unarchive the data and put it into a different array to verify
// that this all works. Unarchive the objects and put them in a new array
unarchivedCars = NSKeyedUnarchiver.unarchiveObjectWithFile(path) as NSArray
// Output the new array
for car : AnyObject in unarchivedCars {
println("Here's a \(car.year) \(car.make) \(car.model)")
}
}
}
Use downcasting in your for loop. The compiler needs to know that car is of type Car and not just AnyObject.
for car in cars as [Car!] {
println("Here's a \(car.year) \(car.make) \(car.model)")
}
I'm trying to add MKAnnotationView to MKMapView but I can't do it… Can anyone help me?
Here is my code:
override func viewDidLoad() {
super.viewDidLoad()
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
var latitude:CLLocationDegrees = locationManager.location.coordinate.latitude
var longitude:CLLocationDegrees = locationManager.location.coordinate.longitude
var homeLati: CLLocationDegrees = 40.01540192
var homeLong: CLLocationDegrees = 20.87901079
var latDelta:CLLocationDegrees = 0.01
var longDelta:CLLocationDegrees = 0.01
var theSpan:MKCoordinateSpan = MKCoordinateSpanMake(latDelta, longDelta)
var myHome:CLLocationCoordinate2D = CLLocationCoordinate2DMake(homeLati, homeLong)
var myLocation:CLLocationCoordinate2D = CLLocationCoordinate2DMake(latitude, longitude)
var theRegion:MKCoordinateRegion = MKCoordinateRegionMake(myLocation, theSpan)
self.theMapView.setRegion(theRegion, animated: true)
self.theMapView.mapType = MKMapType.Hybrid
self.theMapView.showsUserLocation = true
///Red Pin
var myHomePin = MKPointAnnotation()
myHomePin.coordinate = myHome
myHomePin.title = "Home"
myHomePin.subtitle = "Bogdan's home"
self.theMapView.addAnnotation(myHomePin)
var anView:MKAnnotationView = MKAnnotationView()
anView.annotation = myHomePin
anView.image = UIImage(named:"xaxas")
anView.canShowCallout = true
anView.enabled = true
}
You need to implement the viewForAnnotation delegate method and return an MKAnnotationView from there.
Here's an example:
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
if (annotation is MKUserLocation) {
//if annotation is not an MKPointAnnotation (eg. MKUserLocation),
//return nil so map draws default view for it (eg. blue dot)...
return nil
}
let reuseId = "test"
var anView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId)
if anView == nil {
anView = MKAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
anView.image = UIImage(named:"xaxas")
anView.canShowCallout = true
}
else {
//we are re-using a view, update its annotation reference...
anView.annotation = annotation
}
return anView
}
Remember you need to create a plain MKAnnotationView when you want to use your own custom image. The MKPinAnnotationView should only be used for the standard pin annotations.
Also note you should not try to access locationManager.location immediately after calling startUpdatingLocation. It may not be ready yet.
Use the didUpdateLocations delegate method.
You also need to call requestAlwaysAuthorization/requestWhenInUseAuthorization (see Location Services not working in iOS 8).
You have to show annotation, then you have to add the show annotation method:
let annotation = MKPointAnnotation()
mapVew.showAnnotations([annotation], animated: true)
annotation is an instance of MKPointAnnotation.
1- your map view should delegate itself to your view controller
2- you should implement
func mapView(aMapView: MKMapView!, viewForAnnotation annotation: CustomMapPinAnnotation!) -> MKAnnotationView!
this function is called from every annotation added to your Map
3- subclass your annotations to identify them in the viewForAnnotation method
4- in viewForAnnotation, add
if annotation.isKindOfClass(CustomMapPinAnnotation)
{
// change the image here
var pinView = aMapView.dequeueReusableAnnotationViewWithIdentifier(reuseId) as? YourSubclassedAnnotation
pinView!.image = UIImage(contentsOfFile: "xyz")
}