Some MapKit questions in SwiftUI - function

I'm looking to make a setting view in my app where I could define 3 map parameters :
Map center initial position
Map initial zoom
Map initial type
Then show an example map and save in into the #AppStorage
My initial position is determined from an array of data which include :
Place name
Place Latitude
Place Longitude
Latitude and Longitude is find via a function which return a Double? :
func getApLat(ApName: String) -> Double?{
guard let foundAirport = FR_airportsDB.first(where: {$0.Icao == ApName}),
let lat = Double(foundAirport.Latitude) else { return nil }
return lat
}
func getApLong(ApName: String) -> Double?{
guard let foundAirport = FR_airportsDB.first(where: {$0.Icao == ApName}),
let longit = Double(foundAirport.Longitude) else { return nil }
return longit
}
Now my settingMapView is defined as follow :
struct MapOptionView: View {
#State private var showingAlert = false
#State private var latDouble = getApLat(ApName: UserDefaults.standard.string(forKey: "MAP_CenterInit") ?? "LFLI")
#State private var longDouble = getApLong(ApName: UserDefaults.standard.string(forKey: "MAP_CenterInit") ?? "LFLI")
#State private var typeExemple: MKMapType = getTypeFromUD()
#State private var exampleZoom: Int = 5
#AppStorage("MAP_CenterInit") private var MapCenterAirport = ""
#AppStorage("MAP_ZoomInit") private var MapZoom = 2
#AppStorage("MAP_TypeInit") private var MapType = 1
var body: some View {
List{
Section(header: Text("Initial location")){
HStack{
TextField("ICAO", text: $MapCenterAirport)
.padding()
.background(.white)
.cornerRadius(20.0)
.keyboardType(.default)
.textCase(.uppercase)
.onReceive(Just(MapCenterAirport)) { inputValue in
if inputValue.count > 4 {
self.MapCenterAirport.removeLast()
}
}
.disableAutocorrection(true)
.textCase(.uppercase)
if MapCenterAirport != "" {
Button {
//Here I update the center data after get it from the function
latDouble = getApLat(ApName: MapCenterAirport)
longDouble = getApLong(ApName: MapCenterAirport)
} label: {
Text("Check")
}
}
}
}
// Section where Zoom is defined
Section(header: Text("Initial zoom")){
HStack {
if MapZoom == 1{
Text("Zoom : Low")
}else if MapZoom == 2{
Text("Zoom : Medium")
}else if MapZoom == 3{
Text("Zoom : Large")
}
Spacer()
Stepper("", value: $MapZoom, in: 1...3)
}.padding(.vertical)
}
// Section where Type is defined
Section(header: Text("Initial type")){
HStack {
if MapType == 1{
Text("Type : Standard")
}else if MapType == 2{
Text("Type : Satellite")
}else if MapType == 3{
Text("Type : Satellite-flyover")
}else if MapType == 4{
Text("Type : Hybrid")
}else if MapType == 5{
Text("Type : Hybrid-flyover")
}
Spacer()
Stepper("", value: $MapType, in: 1...5)
.frame(width: 60)
.padding(.horizontal, 20)
}.padding(.vertical)
}
// Section where Exemple is Show but need to clickButton to update map ...
Section(header: Text("Example")){
Button {
//Here is zoom adaptation regarding stepper choice from $MapZoom
if MapZoom == 1{
exampleZoom = 60000
}else if MapZoom == 2{
exampleZoom = 110000
}else if MapZoom == 3{
exampleZoom = 160000
}
//Here is type adaptation regarding stepper choice from $MapType
if MapType == 1{
typeExemple = .standard
}else if MapType == 2{
typeExemple = .satellite
}else if MapType == 3{
typeExemple = .satelliteFlyover
}else if MapType == 4{
typeExemple = .hybrid
}else if MapType == 5{
typeExemple = .hybridFlyover
}
} label: {
HStack {
Text("Update example")
}
}
//Here is my Map definition
MapViewSetting(mapType: $typeExemple,
funcLat: $latDouble,
funcLong: $longDouble,
funcZoom: $exampleZoom
)
.edgesIgnoringSafeArea(.all)
.frame(height: 300)
}
}.navigationTitle("Map options")
.navigationBarItems(trailing: Button(action: {
showingAlert = true
//Here we save the 3 value into the AppStorage
MapCenterAirport = MapCenterAirport.uppercased()
UserDefaults.standard.set(self.MapCenterAirport, forKey: "MAP_CenterInit")
UserDefaults.standard.set(self.MapZoom, forKey: "MAP_ZoomInit")
UserDefaults.standard.set(self.MapType, forKey: "MAP_TypeInit")
}, label: {
HStack {
Text("Save")
}
})).alert("Informations saved !\nMust have to restart the app to apply it.", isPresented: $showingAlert) {
Button("OK", role: .cancel) { }
}
}
}
And finally I've my map struct defined as follow after following some post find on this forum.
struct MapViewSetting: UIViewRepresentable {
#Binding var mapType: MKMapType
#Binding var funcLat: Double
#Binding var funcLong: Double
#Binding var funcZoom: Int
func makeUIView(context: Context) -> MKMapView {
let mapView = MKMapView(frame: .zero)
let center = CLLocationCoordinate2D(latitude: funcLat, longitude: funcLong)
let region = MKCoordinateRegion(center: center,
latitudinalMeters: CLLocationDistance(funcZoom),
longitudinalMeters: CLLocationDistance(funcZoom)
)
mapView.setRegion(region, animated: true)
mapView.mapType = mapType
mapView.showsScale = true
mapView.showsTraffic = false
mapView.showsCompass = true
mapView.showsUserLocation = false
mapView.showsBuildings = false
return mapView
}
func updateUIView(_ view: MKMapView, context: Context) {
view.mapType = self.mapType
}
}
But Two big mistake with my code :
First I got an error here :
MapViewSetting(mapType: $typeExemple,
/*regionFunc: $region,*/
funcLat: $latDouble ?? 46.192001,
funcLong: $longDouble ?? 6.26839,
funcZoom: $exampleZoom
)
On funcLat : and funcLong which say : Cannot convert value of type 'Binding<Double?>' to expected argument type 'Binding<Double>'
And If I define manually the Lat and Long in my code to test the two other parameters I could only change the type but not the Zoom ...
Hope to be as clear as I can
Thanks

Related

SwiftUI: How to parse JSON arrays from url using Alamofire?

Environments:macOS Ventura Developer Beta 3Xcode 14.0 beta 3MacBook Pro (13 inch M1, 2020)
References: https://www.youtube.com/watch?v=aMes-DVVJg4https://github.com/TuenTuenna/SwiftUI_Alamofire_RandomUser_tutorial/tree/01_Alamofire
Hello. I'm building a simple ESD for macOS using SwiftUI.
I'm gonna get informations from json by my server, and show informations what it got from api like this:
GameESD_View
Game Informations, installation
but it doesn't show anything like this:
Not working
This is my code. You can see entire code here: https://github.com/bmplatina/BitmapMac
game.json (JSON file to decode and parse, watch http://developer.prodbybitmap.com/game.json):
{
"games": [
{
"gameIndex": 0,
"gameTitle": "The Humanity",
"gamePlatform": "Windows, macOS",
"gameEngine": "Unity",
"gameGenre": "어드벤처",
"gameDeveloper": "입학했더니 무한 팀플과 과제가 쌓여버린 건에 대하여",
"gamePublisher": "Bitmap Production",
"isEarlyAccess": true,
"gameReleasedDate": 20211210,
"gameWebsite": "http://prodbybitmap.com/wiki/The%20Humanity",
"gameImageURL": "http://www.prodbybitmap.com/w/images/9/99/TheHumanityPoster1.png",
"gameDescription": "Desc"
},
{
"gameIndex": 1,
"gameTitle": "OX",
"gamePlatform": "Windows",
"gameEngine": "Unreal Engine 5",
"gameGenre": "몰입형 VR 퍼즐 게임",
"gameDeveloper": "Team. Assertive",
"gamePublisher": "ENTER, Bitmap Production",
"isEarlyAccess": true,
"gameReleasedDate": 20220624,
"gameWebsite": "http://prodbybitmap.com/wiki/OX",
"gameImageURL": "http://www.prodbybitmap.com/w/images/f/f9/OX_CMYK.JPG",
"gameDescription": "Desc"
}
]
}
GameESD_View.swift:
import Foundation
import SwiftUI
import URLImage
struct GameESD_View: View {
#State private var searchField: String = ""
#ObservedObject var gameViewmodel = gameInfoViewmodel()
let gameInfoExam = exampleGameInfo()
let columnLayout = Array(repeating: GridItem(), count: 4)
var body: some View {
VStack {
ZStack {
Rectangle()
.fill(Color.init(hex: "4188F1"))
.frame(height: 42)
.shadow(radius: 4)
HStack {
Spacer()
Image("bitmapWebLogo")
.resizable()
.scaledToFit()
.frame(height: 30)
Spacer()
// if true {
// Text("Online")
// }
// else {
// Text("Offline Mode")
// }
TextField("Filter".localized(), text: $searchField)
}
}
ScrollView {
VStack(alignment: .leading) {
Text("Seoul Institute of the Arts Collection")
.font(.largeTitle)
.bold()
.padding([.top, .leading])
Text("collection.")
.padding(.leading)
Divider()
LazyVGrid(columns: columnLayout, alignment: .center, spacing: 2) {
// List(gameViewmodel.gameInfos) { aGameInfos in }
ForEach(0..<gameViewmodel.gameInfos.count, id: \.self) { aGameInfos in
GameButtons(gameViewmodel.gameInfos[aGameInfos])
}
}
Divider()
Text("Other Games")
.font(.largeTitle)
.bold()
.padding([.top, .leading])
Text("여러 창작자의 다양한 인디 컨텐츠.")
.padding(.leading)
}
}
.navigationTitle("Games".localized())
}
}
}
struct GameButtons: View {
#State private var showingPopover = false
var gameInfos: gameInfo
init(_ gameInfos: gameInfo) {
self.gameInfos = gameInfos
}
var body: some View {
VStack { }
Button {
showingPopover = true
} label: {
ZStack {
Image("unknownImage")
.resizable()
.scaledToFit()
.frame(width: 300)
URLImage(URL(string: gameInfos.gameImageURL)!) { image in
image
.resizable()
.scaledToFit()
.frame(width:300)
}
LinearGradient(gradient: Gradient(colors: [.clear, Color.black.opacity(0.5)]), startPoint: .top, endPoint: .bottom).frame(width: 300, height: 424)
VStack(alignment: .leading) {
Spacer()
Text(gameInfos.gameTitle)
.foregroundColor(.white)
.font(Font.largeTitle)
.bold()
Divider()
Text("Dev".localized() + ": " + gameInfos.gameDeveloper)
.foregroundColor(.white)
}
.frame(width:256)
.padding()
}
.cornerRadius(24)
.shadow(radius: 4)
.padding()
}
.buttonStyle(PlainButtonStyle())
.sheet(isPresented: $showingPopover) {
VStack(alignment: .leading) {
HStack {
VStack(alignment: .leading) {
Text("Bitmap Games")
.font(Font.largeTitle)
.bold()
Text("Bitmap Store".localized())
}
Spacer()
Button(action: { showingPopover = false }) {
Image(systemName: "x.circle")
.font(.title2)
}
.padding()
.background(Color.red)
.foregroundColor(.white)
.clipShape(Capsule())
.buttonStyle(PlainButtonStyle())
}
.padding()
GameDetailsView(gameIndex: gameInfos.gameIndex)
}
.frame(width: 1000, height: 600)
.fixedSize()
}
}
}
struct GameDetailsView: View {
#State private var installAlert = false
let gameInfo: exampleGameInfo = exampleGameInfo()
var gameIndex: Int
var body: some View {
Text("Temp")
}
}
#if DEBUG
struct ESD_Previews: PreviewProvider {
static var previews: some View {
GameESD_View()
// digitalArtsFestivalWebView()
}
}
#endif
FetchGameInformation.swift:
import Foundation
import Combine
import Alamofire
struct gameInfo: Codable, Identifiable {
var id = UUID()
var gameIndex: Int
var gameTitle: String
var gamePlatform: String
var gameEngine: String
var gameGenre: String
var gameDeveloper: String
var gamePublisher: String
var isEarlyAccess: Bool
var gameReleasedDate: Int
var gameWebsite: String
var gameImageURL: String
var gameDescription: String
var gameImage: URL {
get {
URL(string: gameImageURL)!
}
}
static func makeDummy() -> Self {
print(#fileID, #function, #line, "")
return gameInfo(gameIndex: 1, gameTitle: "OX", gamePlatform: "Windows", gameEngine: "Unreal Engine 5", gameGenre: "몰입형 VR 퍼즐 게임", gameDeveloper: "Team. Assertive", gamePublisher: "ENTER, Bitmap Production", isEarlyAccess: true, gameReleasedDate: 20220624, gameWebsite: "http://prodbybitmap.com/wiki/OX", gameImageURL: "http://www.prodbybitmap.com/w/images/f/f9/OX_CMYK.JPG", gameDescription: "This is Description")
}
}
struct gameResult: Codable, CustomStringConvertible {
var games: [gameInfo]
var description: String {
return "gameInfo.count: \(games.count)"
}
}
struct gamesResponse: Codable, CustomStringConvertible {
var games: [gameInfo]
var description: String {
return "games.count: \(games.count) / info : \(games[0].gameTitle)"
}
}
class gameInfoViewmodel: ObservableObject {
// MARK: Properties
var subscription = Set<AnyCancellable>()
#Published var gameInfos = [gameInfo]()
var url = "http://developer.prodbybitmap.com/game.json"
init() {
print(#fileID, #function, #line, "")
fetchGameInfo()
}
func fetchGameInfo() {
print(#fileID, #function, #line, "")
AF.request(url, method: .get)
.publishDecodable(type: gamesResponse.self)
.compactMap { $0.value }
.map { $0.games }
.sink(receiveCompletion: { completion in
print("Datastream Done")
}, receiveValue: { [weak self](receivedValue: [gameInfo]) in
guard let self = self else { return }
print("Data Value: \(receivedValue.description)")
self.gameInfos = receivedValue
}).store(in: &subscription)
}
}

For loop returning only first json Object Flutter

While I tried to fetch data from a JSON am only able to return the first object.
I had mentioned the statement which I think the issue is. I want to return all the available object from the JSON so that I shall display it in a list of cards. I need to return all the JSON objects and pass it to another page containing list of cards. If anyone knows the solution please help me fix it.
Thanks in advance.
import 'dart: convert';
import 'package:flutter/cupertino.dart';
import 'package:tts/tts.dart';
import 'package:wiizboo/service/Chatmsg_service.dart';
import 'package:wiizboo/widget/a%20copy.dart';
import 'package:wiizboo/widget/chat_in_message.dart';
import 'package:wiizboo/widget/chat_out_message.dart';
import 'package:wiizboo/widget/form.dart';
import 'package:wiizboo/widget/image_camera.dart';
import 'package:wiizboo/widget/image_gallery.dart';
class ChatMessageModel with ChangeNotifier {
String data;
List accntdet;
ChatmessageService chatservice = ChatmessageService();
List<Widget> messages = <Widget>[];
ChatMessageModel() {
sendMsg("Hi");
}
Future handlesubmission(String chattext) {
Widget message = new ChatInMessage(
text: chattext,
name: "Me",
type: true,
);
addMsg(message);
sendMsg(chattext);
}
addMsg(Widget msg) {
messages.insert(0, msg);
notifyListeners();
}
sendMsg(String msg) {
chatservice.SendMsg(msg).then((String msg) {
responseBuilder(msg);
});
}
responseBuilder(String msg) {
Widget message;
String identifier = '';
var arr = msg.split("~");
if (arr.length > 1) {
identifier = arr[0];
msg = arr[1];
} else {
msg = arr[0];
}
switch (identifier) {
case 'email_phone':
message = new Forms(onSubmitted: (String val) {
Widget a = new ChatInMessage(
text: val,
name: "Me",
type: true,
);
addMsg(a);
sendMsg(val);
});
break;
case 'selfie':
message = new ImageCamera(onSubmitted: (String imageres) {
Widget b = new ChatInMessage(
text: imageres,
name: "Me",
type: true,
);
sendMsg(imageres);
});
break;
case 'aadhar':
message = new ImageGalery(onSubmitted: (String imageres) {
Widget b = new ChatInMessage(
text: imageres,
name: "Me",
type: true,
);
sendMsg(imageres);
});
break;
case 'account_info':
print(msg);
data = msg;
String receivedJson = data;
List<dynamic> list = json.decode(receivedJson);
accntdet = list;
int l = list.length;
print(l);
//------------ the statement --------//
for (dynamic account in accntdet) {
message = new AccountInfo(
l: l,
iban: account['ibn_no'],
accno: account['account_no'],
sort: account['sort-code'],
currency: account['currency'],
);
}
//----------//
print(message);
break;
default:
message = new ChatOutMessage(
text: msg,
name: "WzBoo..",
);
Tts.speak(msg);
}
addMsg(message);
}
}
Change this bloc
class ChatMessageModel with ChangeNotifier {
String data;
List accntdet;
ChatmessageService chatservice = ChatmessageService();
List<Widget> messages = <Widget>[];
ChatMessageModel() {
sendMsg("Hi");
}
Future handlesubmission(String chattext) {
Widget message = new ChatInMessage(
text: chattext,
name: "Me",
type: true,
);
addMsg(message);
sendMsg(chattext);
}
addMsg(Widget msg) {
messages.insert(0, msg);
notifyListeners();
}
sendMsg(String msg) {
chatservice.SendMsg(msg).then((String msg) {
responseBuilder(msg);
});
}
responseBuilder(String msg) {
Widget message;
String identifier = '';
var arr = msg.split("~");
if (arr.length > 1) {
identifier = arr[0];
msg = arr[1];
} else {
msg = arr[0];
}
switch (identifier) {
case 'email_phone':
message = new Forms(onSubmitted: (String val) {
Widget a = new ChatInMessage(
text: val,
name: "Me",
type: true,
);
addMsg(a);
sendMsg(val);
});
break;
case 'selfie':
message = new ImageCamera(onSubmitted: (String imageres) {
Widget b = new ChatInMessage(
text: imageres,
name: "Me",
type: true,
);
sendMsg(imageres);
});
addMsg(message);
break;
case 'aadhar':
message = new ImageGalery(onSubmitted: (String imageres) {
Widget b = new ChatInMessage(
text: imageres,
name: "Me",
type: true,
);
sendMsg(imageres);
});
break;
case 'account_info':
print(msg);
data = msg;
String receivedJson = data;
List<dynamic> list = json.decode(receivedJson);
accntdet = list;
int l = list.length;
print(l);
//------------ the statement --------//
for (dynamic account in accntdet) {
message = new AccountInfo(
l: l,
iban: account['ibn_no'],
accno: account['account_no'],
sort: account['sort-code'],
currency: account['currency'],
);
print(message);
addMsg(message);
}
//----------//
break;
default:
message = new ChatOutMessage(
text: msg,
name: "WzBoo..",
);
Tts.speak(msg);
addMsg(message);
}
}
}

i used google auto search places API in my ionic app. It start search but my list cannot dismiss after selecting a place

page.html
<ion-searchbar [(ngModel)]="address" (ionCancel)="dismiss()" name="address" id="address"
(ionInput)="searchPlace()" autofocus>
</ion-searchbar>
<ion-list style="margin-top: 20px;" >
<ion-item class="addressList" *ngFor="let item of autocompleteItems"
(click)="selectAddress(item)">
<ion-icon name="pin">
</ion-icon>
<p><small>{{ item.formatted_address }}</small></p>
</ion-item>
</ion-list>
</ion-content>
page.ts
updateSearch(){
console.log(this.address);
let address = this.address;
let me = this;
let location = [];
let element = document.getElementById("map");
let service = new google.maps.places.AutocompleteService();
service.getPlacePredictions({ input: address },
function(predictions, status) {
if (status != google.maps.places.PlacesServiceStatus.OK) {
//this.autocomplete = { input: '' };
this.autocompleteItems = [];
} else {
for (var i = 0; i < predictions.length; i++) {
let googlePlacesService = new google.maps.places.PlacesService(element);
googlePlacesService.getDetails({
reference: predictions[i].reference
},
function(details, status){
if(details){
//setTimeout(() => {
location.push(details);
if(i == predictions.length){
me.autocompleteItems = location;
console.log(location);
}
//console.log(details);
//}, 1000*(i+1);
}
});
}
}
});
}
searchPlace(){
//console.log(this.address);
let address = this.address;
let me = this;
let location = [];
let element = document.getElementById("map");
let service = new google.maps.places.AutocompleteService(this.mapElement, {
types: ['address']
});
service.getPlacePredictions({ input: address },
function(predictions, status) {
if (status != google.maps.places.PlacesServiceStatus.OK) {
this.autocompleteItems = [];
} else {
for (var i = 0; i < predictions.length; i++) {
let googlePlacesService = new google.maps.places.PlacesService(element);
googlePlacesService.getDetails({
reference: predictions[i].reference
}, function(details, status){
if(details){
//setTimeout(() => {
location.push(details);
if(i == predictions.length){
me.autocompleteItems = location;
console.log(location);
}
//console.log(details);
//}, 1000*(i+1);
}
});
}
}
});
}
selectAddress(item){
let data : any;
let custom = data;
this.name = name;
console.log(item)
this.address = item.formatted_address;
if(typeof item.geometry.location.lat != 'function'){
this.start_pos = {
lat: item.geometry.location.lat,
lng: item.geometry.location.lng
};
// console.log( item.geometry.location.lat );
} else {
let data = item.geometry.location.toString();
data = data.replace(/[{()}]/g, '');
let latlngStr = data.split(',', 2);
this.start_pos = {
lat: parseFloat(latlngStr[0]), lng: parseFloat(latlngStr[1])
};
console.log(this.start_pos);
}
//this.viewCtrl.dismiss({lat: this.start_pos.lat, long: this.start_pos.lng, name: name});
}
ionViewDidLoad() {
console.log('ionViewDidLoad createappointment');
}
Whenever I choose an address from list it shows in my searchbar but after that list doesn't disappear and also if i remove the selected address then same issue is also in that case.
How I can dismiss the list after selecting an address from the list. please help me I did not
get where m going wrong in my code. Thank you for your help.
In your selectAddress function, make autocompleteItems empty array at the last. So it gets disappears.
selectAddress(item){
// your code
.
.
..
this.autocompleteItems = [];
}

Logstash: Flatten nested JSON, combine fields inside array

I have a JSON looking like this:
{
"foo": {
"bar": {
"type": "someType",
"id": "ga241ghs"
},
"tags": [
{
"#tagId": "123",
"tagAttributes": {
"attr1": "AAA",
"attr2": "111"
}
},
{
"#tagId": "456",
"tagAttributes": {
"attr1": "BBB",
"attr2": "222"
}
}
]
},
"text": "My text"
}
Actually it's not split to multiple lines (just did it to give a better overview), so it's looking like this:
{"foo":{"bar":{"type":"someType","id":"ga241ghs"},"tags":[{"#tagId":"123","tagAttributes":{"attr1":404,"attr2":416}},{"#tagId":"456","tagAttributes":{"attr1":1096,"attr2":1103}}]},"text":"My text"}
I want to insert this JSON with Logstash to an Elasticsearch index. However, I want to insert a flattened JSON with the fields in the array combined like this:
"foo.bar.tags.tagId": ["123", "456"]
"foo.tags.tagAttributs.attr1": ["AAA", "BBB"]
"foo.tags.tagAttributs.attr2": ["111", "222"]
In total, the data inserted to Elasticsearch should look like this:
"foo.bar.type": "someType"
"foo.bar.id": "ga241ghs"
"foo.tags.tagId": ["123", "456"]
"foo.tags.tagAttributs.attr1": ["AAA", "BBB"]
"foo.tags.tagAttributs.attr2": ["111", "222"]
"foo.text": "My text"
This is my current Logstash .conf; I am able to split the "tags" array, but now I am getting 2 entries as a result.
How can I now join all tagIds to one field, attr1 values of the array to one field, and all attr2 values to another?
input {
file {
codec => json
path => ["/path/to/my/data/*.json"]
mode => "read"
file_completed_action => "log"
file_completed_log_path => ["/path/to/my/logfile"]
sincedb_path => "/dev/null"
}
}
filter {
split {
field => "[foo][tags]"
}
}
output {
stdout { codec => rubydebug }
}
Thanks a lot!
Nice example for my JSON iterator IIFE - no need for complex algos, just pick DepthFirst, sligthly modified path (new "raw" version) and that is it.
In case you like this JS answer, mind ticking accept flag under voting buttons.
In case you want different language, have also C# parser with similar iterators on same GitHub.
var src = {"foo":{"bar":{"type":"someType","id":"ga241ghs"},"tags":[{"#tagId":"123","tagAttributes":{"attr1":"AAA","attr2":"111"}},{"#tagId":"456","tagAttributes":{"attr1":"BBB","attr2":"222"}}],"text":"My text"}};
//console.log(JSON.stringify(src, null, 2));
function traverse(it) {
var dest = {};
var i=0;
do {
if (it.Current().HasStringValue()) {
var pathKey = it.Path(true).join('.');
var check = dest[pathKey];
if (check) {
if (!(check instanceof Array)) dest[pathKey] = [check];
dest[pathKey].push(it.Value());
} else {
dest[pathKey] = it.Value();
}
}
//console.log(it.Level + '\t' + it.Path(1).join('.') + '\t' + it.KeyDots(), (it.Value() instanceof Object) ? "-" : it.Value());
} while (it.DepthFirst());
console.log(JSON.stringify(dest, null, 2));
return dest;
}
/*
* https://github.com/eltomjan/ETEhomeTools/blob/master/HTM_HTA/JSON_Iterator_IIFE.js
* +new raw Path feature
*/
'use strict';
var JNode = (function (jsNode) {
function JNode(_parent, _pred, _key, _value) {
this.parent = _parent;
this.pred = _pred;
this.node = null;
this.next = null;
this.key = _key;
this.value = _value;
}
JNode.prototype.HasOwnKey = function () { return this.key && (typeof this.key != "number"); }
JNode.prototype.HasStringValue = function () { return !(this.value instanceof Object); }
return JNode;
})();
var JIterator = (function (json) {
var root, current, maxLevel = -1;
function JIterator(json, parent) {
if (parent === undefined) parent = null;
var pred = null, localCurrent;
for (var child in json) {
var obj = json[child] instanceof Object;
if (json instanceof Array) child = parseInt(child); // non-associative array
if (!root) root = localCurrent = new JNode(parent, null, child, json[child]);
else {
localCurrent = new JNode(parent, pred, child, obj ? ((json[child] instanceof Array) ? [] : {}) : json[child]);
}
if (pred) pred.next = localCurrent;
if (parent && parent.node == null) parent.node = localCurrent;
pred = localCurrent;
if (obj) {
var memPred = pred;
JIterator(json[child], pred);
pred = memPred;
}
}
if (this) {
current = root;
this.Level = 0;
}
}
JIterator.prototype.Current = function () { return current; }
JIterator.prototype.SetCurrent = function (newCurrent) {
current = newCurrent;
this.Level = 0;
while(newCurrent = newCurrent.parent) this.Level++;
}
JIterator.prototype.Parent = function () {
var retVal = current.parent;
if (retVal == null) return false;
this.Level--;
return current = retVal;
}
JIterator.prototype.Pred = function () {
var retVal = current.pred;
if (retVal == null) return false;
return current = retVal;
}
JIterator.prototype.Node = function () {
var retVal = current.node;
if (retVal == null) return false;
this.Level++;
return current = retVal;
}
JIterator.prototype.Next = function () {
var retVal = current.next;
if (retVal == null) return false;
return current = retVal;
}
JIterator.prototype.Key = function () { return current.key; }
JIterator.prototype.KeyDots = function () { return (typeof (current.key) == "number") ? "" : (current.key + ':'); }
JIterator.prototype.Value = function () { return current.value; }
JIterator.prototype.Reset = function () {
current = root;
this.Level = 0;
}
JIterator.prototype.RawPath = function () {
var steps = [], level = current;
do {
if (level != null && level.value instanceof Object) {
steps.push(level.key + (level.value instanceof Array ? "[]" : "{}"));
} else {
if (level != null) steps.push(level.key);
else break;
}
level = level.parent;
} while (level != null);
var retVal = "";
retVal = steps.reverse();
return retVal;
}
JIterator.prototype.Path = function (raw) {
var steps = [], level = current;
do {
if (level != null && level.value instanceof Object) {
var size = 0;
var items = level.node;
if (typeof (level.key) == "number" && !raw) steps.push('[' + level.key + ']');
else {
if(raw) {
if (typeof (level.key) != "number") steps.push(level.key);
} else {
while (items) {
size++;
items = items.next;
}
var type = (level.value instanceof Array ? "[]" : "{}");
var prev = steps[steps.length - 1];
if (prev && prev[0] == '[') {
var last = prev.length - 1;
if (prev[last] == ']') {
last--;
if (!isNaN(prev.substr(1, last))) {
steps.pop();
size += '.' + prev.substr(1, last);
}
}
}
steps.push(level.key + type[0] + size + type[1]);
}
}
} else {
if (level != null) {
if (typeof (level.key) == "number") steps.push('[' + level.key + ']');
else steps.push(level.key);
}
else break;
}
level = level.parent;
} while (level != null);
var retVal = "";
retVal = steps.reverse();
return retVal;
}
JIterator.prototype.DepthFirst = function () {
if (current == null) return 0; // exit sign
if (current.node != null) {
current = current.node;
this.Level++;
if (maxLevel < this.Level) maxLevel = this.Level;
return 1; // moved down
} else if (current.next != null) {
current = current.next;
return 2; // moved right
} else {
while (current != null) {
if (current.next != null) {
current = current.next;
return 3; // returned up & moved next
}
this.Level--;
current = current.parent;
}
}
return 0; // exit sign
}
JIterator.prototype.BreadthFirst = function () {
if (current == null) return 0; // exit sign
if (current.next) {
current = current.next;
return 1; // moved right
} else if (current.parent) {
var level = this.Level, point = current;
while (this.DepthFirst() && level != this.Level);
if (current) return 2; // returned up & moved next
do {
this.Reset();
level++;
while (this.DepthFirst() && level != this.Level);
if (current) return 3; // returned up & moved next
} while (maxLevel >= level);
return current != null ? 3 : 0;
} else if (current.node) {
current = current.node;
return 3;
} else if (current.pred) {
while (current.pred) current = current.pred;
while (current && !current.node) current = current.next;
if (!current) return null;
else return this.DepthFirst();
}
}
JIterator.prototype.ReadArray = function () {
var retVal = {};
var item = current;
do {
if (item.value instanceof Object) {
if (item.value.length == 0) retVal[item.key] = item.node;
else retVal[item.key] = item;
} else retVal[item.key] = item.value;
item = item.next;
} while (item != null);
return retVal;
}
JIterator.prototype.FindKey = function (key) {
var pos = current;
while (current && current.key != key) this.DepthFirst();
if (current.key == key) {
var retVal = current;
current = pos;
return retVal;
} else {
current = pos;
return null;
}
}
return JIterator;
})();
traverse(new JIterator(src));
Your short JSON version was different, now using this one, which looks like your required results (attrs changed and text moved from root under foo):
{
"foo": {
"bar": {
"type": "someType",
"id": "ga241ghs"
},
"tags": [
{
"#tagId": "123",
"tagAttributes": {
"attr1": "AAA",
"attr2": "111"
}
},
{
"#tagId": "456",
"tagAttributes": {
"attr1": "BBB",
"attr2": "222"
}
}
],
"text": "My text"
}
}
Figured it out how to do it with a Ruby filter directly in Logstash - for all searching for this in future, here is one example on how to do it for #tagId:
filter {
ruby { code => '
i = 0
tagId_array = Array.new
while i < event.get( "[foo][tags]" ).length do
tagId_array = tagId_array.push(event.get( "[foo][tags][" + i.to_s + "][#tagId]" ))
i += 1
end
event.set( "foo.tags.tagId", tagId_array )
'
}
}

There doesn't appear to be support for UITabBar in SwiftUI. Workarounds?

SwiftUI doesn't appear to support UITabBar. How can I integrate that capability?
Merely wrapping the view like one would a (eg) MKMapView, doesn't work because of its need for deep integration with NavigationView. Using UINavigationView is too un-SwiftUI-ish.
The 'TabbedView' is the closest thing. It can be used similar to the following:
struct TabView : View {
#State private var selection = 1
var body: some View {
TabbedView (selection: $selection) {
InboxList()
.tabItemLabel(selection == 1 ? Image("second") : Image("first"))
.tag(1)
PostsList()
.tabItemLabel(Image("first"))
.tag(2)
Spacer()
.tabItemLabel(Image("first"))
.tag(3)
Spacer()
.tabItemLabel(Image("second"))
.tag(4)
}
}
}
If you aren't happy with TabbedView, you can always roll your own! Here's a quick base implementation:
import SwiftUI
struct ContentView : View {
let tabs = [TabItemView(title: "Home", content: { Text("Home page text") }), TabItemView(title: "Other", content: { Text("Other page text") }), TabItemView(title: "Pictures", content: { Text("Pictures page text") })]
var body: some View {
TabBar(tabs: tabs, selectedTab: tabs[0])
}
}
struct TabItemView<Content> : Identifiable where Content : View {
var id = UUID()
var title: String
var content: Content
init(title: String, content: () -> Content) {
self.title = title
self.content = content()
}
var body: _View { content }
typealias Body = Never
}
struct TabBar<Content>: View where Content : View {
let tabButtonHeight: Length = 60
var tabs: [TabItemView<Content>]
#State var selectedTab: TabItemView<Content>
var body: some View {
GeometryReader { geometry in
VStack(spacing: 0) {
self.selectedTab.content.frame(width: geometry.size.width, height: geometry.size.height - self.tabButtonHeight)
Divider()
HStack(spacing: 0) {
ForEach(self.tabs) { tab in
Button(action: { self.selectedTab = tab}) {
Text(tab.title)
}.frame(width: geometry.size.width / CGFloat(Double(self.tabs.count)), height: self.tabButtonHeight)
}
}
.background(Color.gray.opacity(0.4))
}
.frame(width: geometry.size.width, height: geometry.size.height)
}
}
}
UITabBar seems to be working now on Xcode 13.3, SwiftUI 3, iOS15+
It works even though I didn't import UIKit, not sure if that has any effect but it's working for me
struct LandingView: View {
#Binding var selectedTab: String
//hiding tab bar
init(selectedTab: Binding<String>) {
self._selectedTab = selectedTab
UITabBar.appearance().isHidden = true
}
var body: some View {
//Tab view with tabs
TabView(selection: $selectedTab) {
//Views
Home()
.tag("Home")
PlaylistView()
.tag("My Playlists")
HistoryView()
.tag("History")
}
}
}
I misstated the question as I was trying to make ToolBar... below is the code I ended up with... thanks to all.
struct ToolBarItem : Identifiable {
var id = UUID()
var title : String
var imageName : String
var action: () -> Void
}
struct TooledView<Content> : View where Content : View{
var content : Content
var items : [ToolBarItem]
let divider = Color.black.opacity(0.2)
init(items : [ToolBarItem], content: () -> Content){
self.items = items
self.content = content()
}
var body : some View{
VStack(spacing: 0){
self.content
self.divider.frame(height: 1)
ToolBar(items: self.items).frame(height: ToolBar.Height)
}
}
}
struct ToolBar : View{
static let Height : Length = 60
var items : [ToolBarItem]
var body: some View {
GeometryReader { geometry in
HStack(spacing: 0){
ForEach(self.items){ item in
Button(action: item.action){
Image(systemName: item.imageName).imageScale(.large)
Text(item.title).font(.caption)
}.frame(width: geometry.size.width / CGFloat(Double(self.items.count)))
}
}
.frame(height: ToolBar.Height)
.background(Color.gray.opacity(0.10))
}
}
}